| //------------------------------------------------------------------------------------------------------- |
| // 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" |
| #ifndef USING_PAL_STDLIB |
| #include <strsafe.h> |
| #endif |
| #include "ByteCode/ByteCodeApi.h" |
| #include "Exceptions/EvalDisabledException.h" |
| |
| #include "Types/PropertyIndexRanges.h" |
| #include "Types/SimpleDictionaryPropertyDescriptor.h" |
| #include "Types/SimpleDictionaryTypeHandler.h" |
| |
| namespace Js |
| { |
| GlobalObject * GlobalObject::New(ScriptContext * scriptContext) |
| { |
| SimpleDictionaryTypeHandler* globalTypeHandler = SimpleDictionaryTypeHandler::New( |
| scriptContext->GetRecycler(), InitialCapacity, InlineSlotCapacity, sizeof(Js::GlobalObject)); |
| |
| DynamicType* globalType = DynamicType::New( |
| scriptContext, TypeIds_GlobalObject, nullptr, nullptr, globalTypeHandler); |
| |
| GlobalObject* globalObject = RecyclerNewPlus(scriptContext->GetRecycler(), |
| sizeof(Var) * InlineSlotCapacity, GlobalObject, globalType, scriptContext); |
| |
| globalTypeHandler->SetSingletonInstanceIfNeeded(scriptContext->GetRecycler()->CreateWeakReferenceHandle<DynamicObject>(globalObject)); |
| |
| return globalObject; |
| } |
| |
| GlobalObject::GlobalObject(DynamicType * type, ScriptContext* scriptContext) : |
| RootObjectBase(type, scriptContext), |
| directHostObject(nullptr), |
| secureDirectHostObject(nullptr), |
| EvalHelper(&GlobalObject::DefaultEvalHelper), |
| reservedProperties(nullptr) |
| { |
| } |
| |
| void GlobalObject::Initialize(ScriptContext * scriptContext) |
| { |
| Assert(type->javascriptLibrary == nullptr); |
| JavascriptLibrary* localLibrary = RecyclerNewFinalized(scriptContext->GetRecycler(), JavascriptLibrary, this); |
| scriptContext->SetLibrary(localLibrary); |
| type->javascriptLibrary = localLibrary; |
| scriptContext->InitializeCache(); |
| localLibrary->Initialize(scriptContext, this); |
| library = localLibrary; |
| } |
| |
| bool GlobalObject::Is(Var aValue) |
| { |
| return RecyclableObject::Is(aValue) && (RecyclableObject::FromVar(aValue)->GetTypeId() == TypeIds_GlobalObject); |
| } |
| |
| GlobalObject* GlobalObject::FromVar(Var aValue) |
| { |
| AssertMsg(Is(aValue), "Ensure var is actually a 'GlobalObject'"); |
| return static_cast<GlobalObject*>(aValue); |
| } |
| |
| HRESULT GlobalObject::SetDirectHostObject(RecyclableObject* hostObject, RecyclableObject* secureDirectHostObject) |
| { |
| HRESULT hr = S_OK; |
| |
| BEGIN_TRANSLATE_OOM_TO_HRESULT_NESTED |
| { |
| // In fastDOM scenario, we should use the host object to lookup the prototype. |
| this->SetPrototype(library->GetNull()); |
| } |
| END_TRANSLATE_OOM_TO_HRESULT(hr) |
| |
| this->directHostObject = hostObject; |
| this->secureDirectHostObject = secureDirectHostObject; |
| return hr; |
| } |
| |
| RecyclableObject* GlobalObject::GetDirectHostObject() |
| { |
| return this->directHostObject; |
| } |
| |
| RecyclableObject* GlobalObject::GetSecureDirectHostObject() |
| { |
| return this->secureDirectHostObject; |
| } |
| |
| // Converts the global object into the object that should be used as the 'this' parameter. |
| // In a non-hosted environment, the global object (this) is returned. |
| // In a hosted environment, the host object is returned. |
| Var GlobalObject::ToThis() |
| { |
| Var ret; |
| |
| // In fast DOM, we need to give user the secure version of host object |
| if (secureDirectHostObject) |
| { |
| ret = secureDirectHostObject; |
| } |
| else if (hostObject) |
| { |
| // If the global object has the host object, use that as "this" |
| ret = hostObject->GetHostDispatchVar(); |
| Assert(ret); |
| } |
| else |
| { |
| // Otherwise just use the global object |
| ret = this; |
| } |
| |
| return ret; |
| } |
| |
| BOOL GlobalObject::ReserveGlobalProperty(PropertyId propertyId) |
| { |
| if (JavascriptConversion::PropertyQueryFlagsToBoolean(DynamicObject::HasPropertyQuery(propertyId))) |
| { |
| return false; |
| } |
| if (reservedProperties == nullptr) |
| { |
| Recycler* recycler = this->GetScriptContext()->GetRecycler(); |
| reservedProperties = RecyclerNew(recycler, ReservedPropertiesHashSet, recycler, 3); |
| } |
| reservedProperties->AddNew(propertyId); |
| return true; |
| } |
| |
| BOOL GlobalObject::IsReservedGlobalProperty(PropertyId propertyId) |
| { |
| return reservedProperties != nullptr && reservedProperties->Contains(propertyId); |
| } |
| |
| #ifdef IR_VIEWER |
| Var GlobalObject::EntryParseIR(RecyclableObject *function, CallInfo callInfo, ...) |
| { |
| // |
| // retrieve arguments |
| // |
| |
| RUNTIME_ARGUMENTS(args, callInfo); |
| Js::Var codeVar = args[1]; // args[0] is (this) |
| |
| Js::JavascriptString *codeStringVar = nullptr; |
| const char16 *source = nullptr; |
| size_t sourceLength = 0; |
| |
| if (Js::JavascriptString::Is(codeVar)) |
| { |
| codeStringVar = (Js::JavascriptString *)codeVar; |
| source = codeStringVar->GetString(); |
| sourceLength = codeStringVar->GetLength(); |
| } |
| else |
| { |
| AssertMsg(false, "The input to parseIR was not a string."); |
| } |
| |
| // |
| // collect arguments for eval |
| // |
| |
| /* @see NativeCodeGenerator::CodeGen */ |
| ScriptContext *scriptContext = function->GetScriptContext(); |
| |
| // used arbitrary defaults for these, but it seems to work |
| ModuleID moduleID = 0; |
| uint32 grfscr = 0; |
| LPCOLESTR pszTitle = _u(""); |
| BOOL registerDocument = false; |
| |
| BOOL strictMode = true; |
| |
| return IRDumpEvalHelper(scriptContext, source, sourceLength, moduleID, grfscr, |
| pszTitle, registerDocument, FALSE, strictMode); |
| } |
| |
| // TODO remove when refactor |
| Js::PropertyId GlobalObject::CreateProperty(Js::ScriptContext *scriptContext, const char16 *propertyName) |
| { |
| Js::PropertyRecord const *propertyRecord; |
| scriptContext->GetOrAddPropertyRecord(propertyName, (int) wcslen(propertyName), &propertyRecord); |
| Js::PropertyId propertyId = propertyRecord->GetPropertyId(); |
| |
| return propertyId; |
| } |
| |
| // TODO remove when refactor |
| void GlobalObject::SetProperty(Js::DynamicObject *obj, const char16 *propertyName, Js::Var value) |
| { |
| const size_t len = wcslen(propertyName); |
| if (!(len > 0)) |
| { |
| return; |
| } |
| |
| Js::PropertyId id = CreateProperty(obj->GetScriptContext(), propertyName); |
| SetProperty(obj, id, value); |
| } |
| |
| // TODO remove when refactor |
| void GlobalObject::SetProperty(Js::DynamicObject *obj, Js::PropertyId id, Js::Var value) |
| { |
| if (value == NULL) |
| { |
| return; |
| } |
| |
| Js::JavascriptOperators::SetProperty(obj, obj, id, value, obj->GetScriptContext()); |
| } |
| |
| Var GlobalObject::FunctionInfoObjectBuilder(ScriptContext *scriptContext, const char16 *file, |
| const char16 *function, ULONG lineNum, ULONG colNum, |
| uint functionId, Js::Utf8SourceInfo *utf8SrcInfo, Js::Var source) |
| { |
| Js::DynamicObject *fnInfoObj = scriptContext->GetLibrary()->CreateObject(); |
| |
| // create javascript objects for properties |
| Js::Var filenameString = Js::JavascriptString::NewCopyBuffer(file, wcslen(file), scriptContext); |
| Js::Var funcnameString = Js::JavascriptString::NewCopyBuffer(function, wcslen(function), scriptContext); |
| Js::Var lineNumber = Js::JavascriptNumber::ToVar((int64) lineNum, scriptContext); |
| Js::Var colNumber = Js::JavascriptNumber::ToVar((int64) colNum, scriptContext); |
| Js::Var functionIdNumberVar = Js::JavascriptNumber::ToVar(functionId, scriptContext); |
| Js::Var utf8SourceInfoVar = Js::JavascriptNumber::ToVar((int32) utf8SrcInfo, scriptContext); |
| |
| // assign properties to function info object |
| SetProperty(fnInfoObj, _u("filename"), filenameString); |
| SetProperty(fnInfoObj, _u("function"), funcnameString); |
| SetProperty(fnInfoObj, _u("line"), lineNumber); |
| SetProperty(fnInfoObj, _u("col"), colNumber); |
| SetProperty(fnInfoObj, _u("funcId"), functionIdNumberVar); |
| SetProperty(fnInfoObj, _u("utf8SrcInfoPtr"), utf8SourceInfoVar); |
| SetProperty(fnInfoObj, _u("source"), source); |
| |
| return fnInfoObj; |
| } |
| |
| /** |
| * Return a list of functions visible in the current ScriptContext. |
| */ |
| Var GlobalObject::EntryFunctionList(RecyclableObject *function, CallInfo callInfo, ...) |
| { |
| // Note: the typedefs below help make the following code more readable |
| |
| // See: Js::ScriptContext::SourceList (declaration not visible from this file) |
| typedef JsUtil::List<RecyclerWeakReference<Utf8SourceInfo>*, Recycler, false, Js::FreeListedRemovePolicy> SourceList; |
| typedef RecyclerWeakReference<Js::Utf8SourceInfo> Utf8SourceInfoRef; |
| |
| ScriptContext *originalScriptContext = function->GetScriptContext(); |
| ThreadContext *threadContext = originalScriptContext->GetThreadContext(); |
| ScriptContext *scriptContext; |
| |
| int fnCount = 0; |
| for (scriptContext = threadContext->GetScriptContextList(); |
| scriptContext; |
| scriptContext = scriptContext->next) |
| { |
| if (scriptContext->IsClosed()) continue; |
| |
| // |
| // get count of functions in all files in script context |
| // |
| SourceList *sourceList = scriptContext->GetSourceList(); |
| sourceList->Map([&fnCount](uint i, Utf8SourceInfoRef *sourceInfoWeakRef) |
| { |
| Js::Utf8SourceInfo *sourceInfo = sourceInfoWeakRef->Get(); |
| if (sourceInfo == nullptr || sourceInfo->GetIsLibraryCode()) // library code has no source, skip |
| { |
| return; |
| } |
| |
| fnCount += sourceInfo->GetFunctionBodyCount(); |
| }); |
| } |
| |
| #ifdef ENABLE_IR_VIEWER_DBG_DUMP |
| if (Js::Configuration::Global.flags.Verbose) |
| { |
| Output::Print(_u(">> There are %d total functions\n"), fnCount); |
| Output::Flush(); |
| } |
| #endif |
| |
| // |
| // create a javascript array to hold info for all of the functions |
| // |
| Js::JavascriptArray *functionList = originalScriptContext->GetLibrary()->CreateArray(fnCount); |
| |
| int count = 0; |
| for (scriptContext = threadContext->GetScriptContextList(); |
| scriptContext; |
| scriptContext = scriptContext->next) |
| { |
| if (scriptContext->IsClosed()) continue; |
| // if (scriptContext == originalScriptContext) continue; // uncomment to ignore the originalScriptContext |
| |
| SourceList *sourceList = scriptContext->GetSourceList(); |
| sourceList->Map([&fnCount, &count, functionList, scriptContext](uint i, Utf8SourceInfoRef *sourceInfoWeakRef) |
| { |
| Js::Utf8SourceInfo *sourceInfo = sourceInfoWeakRef->Get(); |
| if (sourceInfo == nullptr || sourceInfo->GetIsLibraryCode()) // library code has no source, skip |
| { |
| return; |
| } |
| |
| const SRCINFO *srcInfo = sourceInfo->GetSrcInfo(); |
| SourceContextInfo *srcContextInfo = srcInfo->sourceContextInfo; |
| |
| // |
| // get URL of source file |
| // |
| char16 filenameBuffer[128]; // hold dynamically built filename |
| char16 const *srcFileUrl = NULL; |
| if (!srcContextInfo->IsDynamic()) |
| { |
| Assert(srcContextInfo->url != NULL); |
| srcFileUrl = srcContextInfo->url; |
| } |
| else |
| { |
| StringCchPrintf(filenameBuffer, 128, _u("[dynamic(hash:%u)]"), srcContextInfo->hash); |
| srcFileUrl = filenameBuffer; |
| } |
| |
| sourceInfo->MapFunction([scriptContext, &count, functionList, srcFileUrl](Js::FunctionBody *functionBody) |
| { |
| char16 const *functionName = functionBody->GetExternalDisplayName(); |
| |
| ULONG lineNum = functionBody->GetLineNumber(); |
| ULONG colNum = functionBody->GetColumnNumber(); |
| |
| uint funcId = functionBody->GetLocalFunctionId(); |
| Js::Utf8SourceInfo *utf8SrcInfo = functionBody->GetUtf8SourceInfo(); |
| if (utf8SrcInfo == nullptr) |
| { |
| return; |
| } |
| |
| Assert(utf8SrcInfo->FindFunction(funcId) == functionBody); |
| |
| BufferStringBuilder builder(functionBody->LengthInChars(), scriptContext); |
| utf8::DecodeOptions options = utf8SrcInfo->IsCesu8() ? utf8::doAllowThreeByteSurrogates : utf8::doDefault; |
| utf8::DecodeInto(builder.DangerousGetWritableBuffer(), functionBody->GetSource(), functionBody->LengthInChars(), options); |
| Var cachedSourceString = builder.ToString(); |
| |
| Js::Var obj = FunctionInfoObjectBuilder(scriptContext, srcFileUrl, |
| functionName, lineNum, colNum, funcId, utf8SrcInfo, cachedSourceString); |
| functionList->SetItem(count, obj, Js::PropertyOperationFlags::PropertyOperation_None); |
| |
| ++count; |
| }); |
| }); |
| } |
| |
| #ifdef ENABLE_IR_VIEWER_DBG_DUMP |
| if (Js::Configuration::Global.flags.Verbose) |
| { |
| Output::Print(_u(">> Returning from functionList()\n")); |
| Output::Flush(); |
| } |
| #endif |
| |
| return functionList; |
| } |
| |
| /** |
| * Return the parsed IR for the given function. |
| */ |
| Var GlobalObject::EntryRejitFunction(RecyclableObject *function, CallInfo callInfo, ...) |
| { |
| #ifdef ENABLE_IR_VIEWER_DBG_DUMP |
| if (Js::Configuration::Global.flags.Verbose) |
| { |
| Output::Print(_u(">> Entering rejitFunction()\n")); |
| Output::Flush(); |
| } |
| #endif |
| |
| // |
| // retrieve and coerce arguments |
| // |
| |
| RUNTIME_ARGUMENTS(args, callInfo); // args[0] is (callInfo) |
| Js::Var jsVarUtf8SourceInfo = args[1]; |
| Js::Var jsVarFunctionId = args[2]; |
| |
| #ifdef ENABLE_IR_VIEWER_DBG_DUMP |
| if (Js::Configuration::Global.flags.Verbose) |
| { |
| Output::Print(_u("jsVarUtf8SourceInfo: %d (0x%08X)\n"), jsVarUtf8SourceInfo, jsVarUtf8SourceInfo); |
| Output::Print(_u("jsVarFunctionId: %d (0x%08X)\n"), jsVarFunctionId, jsVarFunctionId); |
| } |
| #endif |
| |
| Js::JavascriptNumber *jsUtf8SourceInfoNumber = NULL; |
| Js::JavascriptNumber *jsFunctionIdNumber = NULL; |
| int32 utf8SourceInfoNumber = 0; // null |
| int32 functionIdNumber = -1; // start with invalid function id |
| |
| // extract value of jsVarUtf8SourceInfo |
| if (Js::TaggedInt::Is(jsVarUtf8SourceInfo)) |
| { |
| utf8SourceInfoNumber = (int32)TaggedInt::ToInt64(jsVarUtf8SourceInfo); // REVIEW: just truncate? |
| } |
| else if (Js::JavascriptNumber::Is(jsVarUtf8SourceInfo)) |
| { |
| jsUtf8SourceInfoNumber = (Js::JavascriptNumber *)jsVarUtf8SourceInfo; |
| utf8SourceInfoNumber = (int32)JavascriptNumber::GetValue(jsUtf8SourceInfoNumber); // REVIEW: just truncate? |
| } |
| else |
| { |
| // should not reach here |
| AssertMsg(false, "Failed to extract value for jsVarUtf8SourceInfo as either TaggedInt or JavascriptNumber.\n"); |
| } |
| |
| // extract value of jsVarFunctionId |
| if (Js::TaggedInt::Is(jsVarFunctionId)) |
| { |
| functionIdNumber = (int32)TaggedInt::ToInt64(jsVarFunctionId); // REVIEW: just truncate? |
| } |
| else if (Js::JavascriptNumber::Is(jsVarFunctionId)) |
| { |
| jsFunctionIdNumber = (Js::JavascriptNumber *)jsVarFunctionId; |
| functionIdNumber = (int32)JavascriptNumber::GetValue(jsFunctionIdNumber); // REVIEW: just truncate? |
| } |
| else |
| { |
| // should not reach here |
| AssertMsg(false, "Failed to extract value for jsVarFunctionId as either TaggedInt or JavascriptNumber.\n"); |
| } |
| |
| #ifdef ENABLE_IR_VIEWER_DBG_DUMP |
| if (Js::Configuration::Global.flags.Verbose) |
| { |
| Output::Print(_u("utf8SourceInfoNumber (value): %d (0x%08X)\n"), utf8SourceInfoNumber, utf8SourceInfoNumber); |
| Output::Print(_u("jsVarFunctionId (value): %d (0x%08X)\n"), functionIdNumber, functionIdNumber); |
| Output::Print(_u(">> Executing rejitFunction(%d, %d)\n"), utf8SourceInfoNumber, functionIdNumber); |
| Output::Flush(); |
| } |
| #endif |
| |
| // |
| // recover functionBody |
| // |
| |
| Js::Utf8SourceInfo *sourceInfo = (Js::Utf8SourceInfo *)(utf8SourceInfoNumber); |
| if (sourceInfo == NULL) |
| { |
| return NULL; |
| } |
| |
| Js::FunctionBody *functionBody = sourceInfo->FindFunction((Js::LocalFunctionId)functionIdNumber); |
| |
| // |
| // rejit the function body |
| // |
| |
| Js::ScriptContext *scriptContext = function->GetScriptContext(); |
| NativeCodeGenerator *nativeCodeGenerator = scriptContext->GetNativeCodeGenerator(); |
| |
| if (!functionBody || !functionBody->GetByteCode()) |
| { |
| return scriptContext->GetLibrary()->GetUndefined(); |
| } |
| |
| Js::Var retVal = RejitIRViewerFunction(nativeCodeGenerator, functionBody, scriptContext); |
| |
| // |
| // return the parsed IR object |
| // |
| |
| #ifdef ENABLE_IR_VIEWER_DBG_DUMP |
| if (Js::Configuration::Global.flags.Verbose) |
| { |
| Output::Print(_u("rejitFunction - retVal: 0x%08X\n"), retVal); |
| Output::Flush(); |
| } |
| #endif |
| |
| return retVal; |
| } |
| |
| #endif /* IR_VIEWER */ |
| Var GlobalObject::EntryEvalHelper(ScriptContext* scriptContext, RecyclableObject* function, CallInfo callInfo, Js::Arguments& args) |
| { |
| FrameDisplay* environment = (FrameDisplay*)&NullFrameDisplay; |
| ModuleID moduleID = kmodGlobal; |
| BOOL strictMode = FALSE; |
| // TODO: Handle call from global scope, strict mode |
| BOOL isIndirect = FALSE; |
| |
| if (Js::CallInfo::isDirectEvalCall(args.Info.Flags)) |
| { |
| // This was recognized as an eval call at compile time. The last one or two args are internal to us. |
| // Argcount will be one of the following when called from global code |
| // - eval("...") : argcount 3 : this, evalString, frameDisplay |
| // - eval.call("..."): argcount 2 : this(which is string) , frameDisplay |
| if (args.Info.Count >= 2) |
| { |
| environment = (FrameDisplay*)(args[args.Info.Count - 1]); |
| |
| // Check for a module root passed from the caller. If it's there, use its module ID to compile the eval. |
| // when called inside a module root, module root would be added before the frame display in above scenarios |
| |
| // ModuleRoot is optional |
| // - eval("...") : argcount 3/4 : this, evalString , [module root], frameDisplay |
| // - eval.call("..."): argcount 2/3 : this(which is string) , [module root], frameDisplay |
| |
| strictMode = environment->GetStrictMode(); |
| |
| if (args.Info.Count >= 3 && JavascriptOperators::GetTypeId(args[args.Info.Count - 2]) == TypeIds_ModuleRoot) |
| { |
| moduleID = ((Js::ModuleRoot*)(RecyclableObject::FromVar(args[args.Info.Count - 2])))->GetModuleID(); |
| args.Info.Count--; |
| } |
| args.Info.Count--; |
| } |
| } |
| else |
| { |
| // This must be an indirect "eval" call that we didn't detect at compile time. |
| // Pass null as the environment, which will force all lookups in the eval code |
| // to use the root for the current module. |
| // Also pass "null" for "this", which will force the callee to use the current module root. |
| isIndirect = !PHASE_OFF1(Js::FastIndirectEvalPhase); |
| } |
| |
| return GlobalObject::VEval(function->GetLibrary(), environment, moduleID, !!strictMode, !!isIndirect, args, |
| /* isLibraryCode = */ false, /* registerDocument */ true, /*additionalGrfscr */ 0); |
| } |
| |
| Var GlobalObject::EntryEvalRestrictedMode(RecyclableObject* function, CallInfo callInfo, ...) |
| { |
| PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault); |
| |
| RUNTIME_ARGUMENTS(args, callInfo); |
| Assert(!(callInfo.Flags & CallFlags_New)); |
| |
| JavascriptLibrary* library = function->GetLibrary(); |
| ScriptContext* scriptContext = library->GetScriptContext(); |
| |
| scriptContext->CheckEvalRestriction(); |
| |
| return EntryEvalHelper(scriptContext, function, callInfo, args); |
| } |
| |
| // This function is used to decipher eval function parameters and we don't want the stack arguments optimization by C++ compiler so turning off the optimization |
| Var GlobalObject::EntryEval(RecyclableObject* function, CallInfo callInfo, ...) |
| { |
| PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault); |
| |
| RUNTIME_ARGUMENTS(args, callInfo); |
| Assert(!(callInfo.Flags & CallFlags_New)); |
| |
| JavascriptLibrary* library = function->GetLibrary(); |
| ScriptContext* scriptContext = library->GetScriptContext(); |
| |
| return EntryEvalHelper(scriptContext, function, callInfo, args); |
| } |
| |
| Var GlobalObject::VEval(JavascriptLibrary* library, FrameDisplay* environment, ModuleID moduleID, bool strictMode, bool isIndirect, |
| Arguments& args, bool isLibraryCode, bool registerDocument, uint32 additionalGrfscr) |
| { |
| Assert(library); |
| ScriptContext* scriptContext = library->GetScriptContext(); |
| |
| unsigned argCount = args.Info.Count; |
| |
| AssertMsg(argCount > 0, "Should always have implicit 'this'"); |
| |
| bool doRegisterDocument = registerDocument & !isLibraryCode; |
| Var varThis = library->GetUndefined(); |
| |
| if (argCount < 2) |
| { |
| return library->GetUndefined(); |
| } |
| |
| Var evalArg = args[1]; |
| if (!JavascriptString::Is(evalArg)) |
| { |
| // "If x is not a string value, return x." |
| return evalArg; |
| } |
| |
| // It might happen that no script parsed on this context (scriptContext) till now, |
| // so this Eval acts as the first source compile for scriptContext, transition to debugMode as needed |
| scriptContext->TransitionToDebugModeIfFirstSource(/* utf8SourceInfo = */ nullptr); |
| |
| JavascriptString *argString = JavascriptString::FromVar(evalArg); |
| ScriptFunction *pfuncScript; |
| char16 const * sourceString = argString->GetSz(); |
| charcount_t sourceLen = argString->GetLength(); |
| FastEvalMapString key(sourceString, sourceLen, moduleID, strictMode, isLibraryCode); |
| bool found = scriptContext->IsInEvalMap(key, isIndirect, &pfuncScript); |
| if (!found || (!isIndirect && pfuncScript->GetEnvironment() != &NullFrameDisplay)) |
| { |
| uint32 grfscr = additionalGrfscr | fscrReturnExpression | fscrEval | fscrEvalCode | fscrGlobalCode; |
| |
| if (isLibraryCode) |
| { |
| grfscr |= fscrIsLibraryCode; |
| } |
| |
| pfuncScript = library->GetGlobalObject()->EvalHelper(scriptContext, argString->GetSz(), argString->GetLength(), moduleID, |
| grfscr, Constants::EvalCode, doRegisterDocument, isIndirect, strictMode); |
| |
| if (!found) |
| { |
| scriptContext->AddToEvalMap(key, isIndirect, pfuncScript); |
| } |
| } |
| |
| #ifdef ENABLE_DEBUG_CONFIG_OPTIONS |
| else |
| { |
| Js::Utf8SourceInfo* utf8SourceInfo = pfuncScript->GetFunctionBody()->GetUtf8SourceInfo(); |
| if (scriptContext->IsScriptContextInDebugMode() && !utf8SourceInfo->GetIsLibraryCode() && !utf8SourceInfo->IsInDebugMode()) |
| { |
| // Identifying if any non library function escaped for not being in debug mode. |
| Throw::FatalInternalError(); |
| } |
| } |
| #endif |
| |
| #if ENABLE_TTD |
| // |
| //TODO: We may (probably?) want to use the debugger source rundown functionality here instead |
| // |
| if(!isLibraryCode && pfuncScript != nullptr && (scriptContext->IsTTDRecordModeEnabled() || scriptContext->ShouldPerformReplayAction())) |
| { |
| //Make sure we have the body and text information available |
| FunctionBody* globalBody = TTD::JsSupport::ForceAndGetFunctionBody(pfuncScript->GetParseableFunctionInfo()); |
| if(!scriptContext->TTDContextInfo->IsBodyAlreadyLoadedAtTopLevel(globalBody)) |
| { |
| uint32 bodyIdCtr = 0; |
| |
| if(scriptContext->IsTTDRecordModeEnabled()) |
| { |
| const TTD::NSSnapValues::TopLevelEvalFunctionBodyResolveInfo* tbfi = scriptContext->GetThreadContext()->TTDLog->AddEvalFunction(globalBody, moduleID, sourceString, sourceLen, additionalGrfscr, registerDocument, isIndirect, strictMode); |
| |
| //We always want to register the top-level load but we don't always need to log the event |
| if(scriptContext->ShouldPerformRecordAction()) |
| { |
| scriptContext->GetThreadContext()->TTDLog->RecordTopLevelCodeAction(tbfi->TopLevelBase.TopLevelBodyCtr); |
| } |
| |
| bodyIdCtr = tbfi->TopLevelBase.TopLevelBodyCtr; |
| } |
| |
| if(scriptContext->ShouldPerformReplayAction()) |
| { |
| bodyIdCtr = scriptContext->GetThreadContext()->TTDLog->ReplayTopLevelCodeAction(); |
| } |
| |
| //walk global body to (1) add functions to pin set (2) build parent map |
| scriptContext->TTDContextInfo->ProcessFunctionBodyOnLoad(globalBody, nullptr); |
| scriptContext->TTDContextInfo->RegisterEvalScript(globalBody, bodyIdCtr); |
| |
| if(scriptContext->ShouldPerformRecordOrReplayAction()) |
| { |
| globalBody->GetUtf8SourceInfo()->SetSourceInfoForDebugReplay_TTD(bodyIdCtr); |
| } |
| |
| if(scriptContext->ShouldPerformDebuggerAction()) |
| { |
| scriptContext->GetThreadContext()->TTDExecutionInfo->ProcessScriptLoad(scriptContext, bodyIdCtr, globalBody, globalBody->GetUtf8SourceInfo(), nullptr); |
| } |
| } |
| } |
| #endif |
| |
| //We shouldn't be serializing eval functions; unless with -ForceSerialized flag |
| if (CONFIG_FLAG(ForceSerialized)) { |
| pfuncScript->GetFunctionProxy()->EnsureDeserialized(); |
| } |
| |
| if (pfuncScript->GetFunctionBody()->GetHasThis()) |
| { |
| // The eval expression refers to "this" |
| if (args.Info.Flags & CallFlags_ExtraArg) |
| { |
| JavascriptFunction* pfuncCaller; |
| JavascriptStackWalker::GetCaller(&pfuncCaller, scriptContext); |
| // If we are non-hidden call to eval then look for the "this" object in the frame display if the caller is a lambda else get "this" from the caller's frame. |
| |
| FunctionInfo* functionInfo = pfuncCaller->GetFunctionInfo(); |
| if (functionInfo != nullptr && (functionInfo->IsLambda() || functionInfo->IsClassConstructor())) |
| { |
| Var defaultInstance = (moduleID == kmodGlobal) ? JavascriptOperators::OP_LdRoot(scriptContext)->ToThis() : (Var)JavascriptOperators::GetModuleRoot(moduleID, scriptContext); |
| varThis = JavascriptOperators::OP_GetThisScoped(environment, defaultInstance, scriptContext); |
| UpdateThisForEval(varThis, moduleID, scriptContext, strictMode); |
| } |
| else |
| { |
| JavascriptStackWalker::GetThis(&varThis, moduleID, scriptContext); |
| UpdateThisForEval(varThis, moduleID, scriptContext, strictMode); |
| } |
| } |
| else |
| { |
| // The expression, which refers to "this", is evaluated by an indirect eval. |
| // Set "this" to the current module root. |
| varThis = JavascriptOperators::OP_GetThis(scriptContext->GetLibrary()->GetUndefined(), moduleID, scriptContext); |
| } |
| } |
| |
| if (pfuncScript->HasSuperReference()) |
| { |
| // Indirect evals cannot have a super reference. |
| if (!(args.Info.Flags & CallFlags_ExtraArg)) |
| { |
| JavascriptError::ThrowSyntaxError(scriptContext, ERRSuperInIndirectEval, _u("super")); |
| } |
| } |
| |
| return library->GetGlobalObject()->ExecuteEvalParsedFunction(pfuncScript, environment, varThis, scriptContext); |
| } |
| |
| void GlobalObject::UpdateThisForEval(Var &varThis, ModuleID moduleID, ScriptContext *scriptContext, BOOL strictMode) |
| { |
| if (strictMode) |
| { |
| varThis = JavascriptOperators::OP_StrictGetThis(varThis, scriptContext); |
| } |
| else |
| { |
| varThis = JavascriptOperators::OP_GetThisNoFastPath(varThis, moduleID, scriptContext); |
| } |
| } |
| |
| |
| Var GlobalObject::ExecuteEvalParsedFunction(ScriptFunction *pfuncScript, FrameDisplay* environment, Var &varThis, ScriptContext *scriptContext) |
| { |
| Assert(pfuncScript != nullptr); |
| |
| pfuncScript->SetEnvironment(environment); |
| //This function is supposed to be deserialized |
| Assert(pfuncScript->GetFunctionBody()); |
| if (pfuncScript->GetFunctionBody()->GetFuncEscapes()) |
| { |
| // Executing the eval causes the scope chain to escape. |
| pfuncScript->InvalidateCachedScopeChain(); |
| } |
| Var varResult = CALL_FUNCTION(scriptContext->GetThreadContext(), pfuncScript, CallInfo(CallFlags_Eval, 1), varThis); |
| pfuncScript->SetEnvironment((FrameDisplay*)&NullFrameDisplay); |
| return varResult; |
| } |
| |
| #ifdef ENABLE_SCRIPT_PROFILING |
| ScriptFunction* GlobalObject::ProfileModeEvalHelper(ScriptContext* scriptContext, const char16 *source, int sourceLength, ModuleID moduleID, uint32 grfscr, LPCOLESTR pszTitle, BOOL registerDocument, BOOL isIndirect, BOOL strictMode) |
| { |
| #if ENABLE_DEBUG_CONFIG_OPTIONS |
| char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE]; |
| #endif |
| ScriptFunction *pEvalFunction = DefaultEvalHelper(scriptContext, source, sourceLength, moduleID, grfscr, pszTitle, registerDocument, isIndirect, strictMode); |
| Assert(pEvalFunction); |
| Js::FunctionProxy *proxy = pEvalFunction->GetFunctionProxy(); |
| Assert(proxy); |
| |
| OUTPUT_TRACE(Js::ScriptProfilerPhase, _u("GlobalObject::ProfileModeEvalHelper FunctionNumber : %s, Entrypoint : 0x%08X IsFunctionDefer : %d\n"), |
| proxy->GetDebugNumberSet(debugStringBuffer), pEvalFunction->GetEntryPoint(), proxy->IsDeferred()); |
| |
| if (proxy->IsDeferred()) |
| { |
| // This could happen if the top level function is marked as deferred, we need to parse this to generate the script compile information (RegisterScript depends on that) |
| Js::JavascriptFunction::DeferredParse(&pEvalFunction); |
| proxy = pEvalFunction->GetFunctionProxy(); |
| } |
| |
| scriptContext->RegisterScript(proxy); |
| |
| return pEvalFunction; |
| } |
| #endif |
| |
| void GlobalObject::ValidateSyntax(ScriptContext* scriptContext, const char16 *source, int sourceLength, bool isGenerator, bool isAsync, void (Parser::*validateSyntax)()) |
| { |
| Assert(sourceLength >= 0); |
| |
| HRESULT hr = S_OK; |
| HRESULT hrParser = E_FAIL; |
| CompileScriptException se; |
| |
| BEGIN_LEAVE_SCRIPT_INTERNAL(scriptContext); |
| BEGIN_TRANSLATE_EXCEPTION_TO_HRESULT |
| { |
| ArenaAllocator tempAlloc(_u("ValidateSyntaxArena"), scriptContext->GetThreadContext()->GetPageAllocator(), Throw::OutOfMemory); |
| |
| size_t cchSource = sourceLength; |
| size_t cbUtf8Buffer = (cchSource + 1) * 3; |
| LPUTF8 utf8Source = AnewArray(&tempAlloc, utf8char_t, cbUtf8Buffer); |
| Assert(cchSource < MAXLONG); |
| size_t cbSource = utf8::EncodeIntoAndNullTerminate(utf8Source, source, static_cast< charcount_t >(cchSource)); |
| utf8Source = reinterpret_cast< LPUTF8 >( tempAlloc.Realloc(utf8Source, cbUtf8Buffer, cbSource + 1) ); |
| |
| Parser parser(scriptContext); |
| hrParser = parser.ValidateSyntax(utf8Source, cbSource, isGenerator, isAsync, &se, validateSyntax); |
| } |
| END_TRANSLATE_EXCEPTION_TO_HRESULT(hr); |
| END_LEAVE_SCRIPT_INTERNAL(scriptContext); |
| |
| if (FAILED(hr)) |
| { |
| if (hr == E_OUTOFMEMORY) |
| { |
| JavascriptError::ThrowOutOfMemoryError(scriptContext); |
| } |
| if (hr == VBSERR_OutOfStack) |
| { |
| JavascriptError::ThrowStackOverflowError(scriptContext); |
| } |
| } |
| if (!SUCCEEDED(hrParser)) |
| { |
| hrParser = SCRIPT_E_RECORDED; |
| EXCEPINFO ei; |
| se.GetError(&hrParser, &ei); |
| |
| ErrorTypeEnum errorType; |
| switch ((HRESULT)ei.scode) |
| { |
| #define RT_ERROR_MSG(name, errnum, str1, str2, jst, errorNumSource) \ |
| case name: \ |
| errorType = jst; \ |
| break; |
| #define RT_PUBLICERROR_MSG(name, errnum, str1, str2, jst, errorNumSource) RT_ERROR_MSG(name, errnum, str1, str2, jst, errorNumSource) |
| #include "rterrors.h" |
| #undef RT_PUBLICERROR_MSG |
| #undef RT_ERROR_MSG |
| default: |
| errorType = kjstSyntaxError; |
| } |
| JavascriptError::MapAndThrowError(scriptContext, ei.scode, errorType, &ei); |
| } |
| } |
| |
| ScriptFunction* GlobalObject::DefaultEvalHelper(ScriptContext* scriptContext, const char16 *source, int sourceLength, ModuleID moduleID, uint32 grfscr, LPCOLESTR pszTitle, BOOL registerDocument, BOOL isIndirect, BOOL strictMode) |
| { |
| Assert(sourceLength >= 0); |
| AnalysisAssert(scriptContext); |
| if (scriptContext->GetThreadContext()->EvalDisabled()) |
| { |
| throw Js::EvalDisabledException(); |
| } |
| |
| #ifdef PROFILE_EXEC |
| scriptContext->ProfileBegin(Js::EvalCompilePhase); |
| #endif |
| void * frameAddr = nullptr; |
| GET_CURRENT_FRAME_ID(frameAddr); |
| |
| HRESULT hr = S_OK; |
| HRESULT hrParser = S_OK; |
| HRESULT hrCodeGen = S_OK; |
| CompileScriptException se; |
| Js::ParseableFunctionInfo * funcBody = NULL; |
| |
| BEGIN_LEAVE_SCRIPT_INTERNAL(scriptContext); |
| BEGIN_TRANSLATE_EXCEPTION_TO_HRESULT |
| { |
| uint cchSource = sourceLength; |
| size_t cbUtf8Buffer = (cchSource + 1) * 3; |
| |
| ArenaAllocator tempArena(_u("EvalHelperArena"), scriptContext->GetThreadContext()->GetPageAllocator(), Js::Throw::OutOfMemory); |
| LPUTF8 utf8Source = AnewArray(&tempArena, utf8char_t, cbUtf8Buffer); |
| |
| Assert(cchSource < MAXLONG); |
| size_t cbSource = utf8::EncodeIntoAndNullTerminate(utf8Source, source, static_cast< charcount_t >(cchSource)); |
| Assert(cbSource + 1 <= cbUtf8Buffer); |
| |
| SRCINFO const * pSrcInfo = scriptContext->GetModuleSrcInfo(moduleID); |
| // Source Info objects are kept alive by the function bodies that are referencing it |
| // The function body is created in GenerateByteCode but the source info isn't passed in, only the index |
| // So we need to pin it here (TODO: Change GenerateByteCode to take in the sourceInfo itself) |
| ENTER_PINNED_SCOPE(Utf8SourceInfo, sourceInfo); |
| sourceInfo = Utf8SourceInfo::New(scriptContext, utf8Source, cchSource, |
| cbSource, pSrcInfo, ((grfscr & fscrIsLibraryCode) != 0)); |
| |
| Parser parser(scriptContext, strictMode); |
| bool forceNoNative = false; |
| |
| ParseNodePtr parseTree; |
| |
| SourceContextInfo * sourceContextInfo = pSrcInfo->sourceContextInfo; |
| ULONG deferParseThreshold = Parser::GetDeferralThreshold(sourceContextInfo->IsSourceProfileLoaded()); |
| if ((ULONG)sourceLength > deferParseThreshold && !PHASE_OFF1(Phase::DeferParsePhase)) |
| { |
| // Defer function bodies declared inside large dynamic blocks. |
| grfscr |= fscrDeferFncParse; |
| } |
| |
| grfscr = grfscr | fscrDynamicCode; |
| |
| // fscrEval signifies direct eval in parser |
| hrParser = parser.ParseCesu8Source(&parseTree, utf8Source, cbSource, isIndirect ? grfscr & ~fscrEval : grfscr, &se, &sourceContextInfo->nextLocalFunctionId, |
| sourceContextInfo); |
| sourceInfo->SetParseFlags(grfscr); |
| |
| if (SUCCEEDED(hrParser) && parseTree) |
| { |
| // This keeps function bodies generated by the byte code alive till we return |
| Js::AutoDynamicCodeReference dynamicFunctionReference(scriptContext); |
| |
| Assert(cchSource < MAXLONG); |
| uint sourceIndex = scriptContext->SaveSourceNoCopy(sourceInfo, cchSource, true); |
| |
| // Tell byte code gen not to attempt to interact with the caller's context if this is indirect eval. |
| // TODO: Handle strict mode. |
| if (isIndirect && |
| !strictMode && |
| !parseTree->sxFnc.GetStrictMode()) |
| { |
| grfscr &= ~fscrEval; |
| } |
| hrCodeGen = GenerateByteCode(parseTree, grfscr, scriptContext, &funcBody, sourceIndex, forceNoNative, &parser, &se); |
| sourceInfo->SetByteCodeGenerationFlags(grfscr); |
| } |
| |
| LEAVE_PINNED_SCOPE(); |
| } |
| END_TRANSLATE_EXCEPTION_TO_HRESULT(hr); |
| END_LEAVE_SCRIPT_INTERNAL(scriptContext); |
| |
| |
| #ifdef PROFILE_EXEC |
| scriptContext->ProfileEnd(Js::EvalCompilePhase); |
| #endif |
| THROW_KNOWN_HRESULT_EXCEPTIONS(hr, scriptContext); |
| |
| if (!SUCCEEDED(hrParser)) |
| { |
| JavascriptError::ThrowParserError(scriptContext, hrParser, &se); |
| } |
| else if (!SUCCEEDED(hrCodeGen)) |
| { |
| Assert(hrCodeGen == SCRIPT_E_RECORDED); |
| hrCodeGen = se.ei.scode; |
| /* |
| * VBSERR_OutOfStack is of type kjstError but we throw a (more specific) StackOverflowError when a hard stack |
| * overflow occurs. To keep the behavior consistent I'm special casing it here. |
| */ |
| se.Free(); |
| if (hrCodeGen == VBSERR_OutOfStack) |
| { |
| JavascriptError::ThrowStackOverflowError(scriptContext); |
| } |
| else if (hrCodeGen == JSERR_AsmJsCompileError) |
| { |
| // if asm.js compilation succeeded, retry with asm.js disabled |
| grfscr |= fscrNoAsmJs; |
| return DefaultEvalHelper(scriptContext, source, sourceLength, moduleID, grfscr, pszTitle, registerDocument, isIndirect, strictMode); |
| } |
| JavascriptError::MapAndThrowError(scriptContext, hrCodeGen); |
| } |
| else |
| { |
| |
| Assert(funcBody != nullptr); |
| funcBody->SetDisplayName(pszTitle); |
| |
| // Set the functionbody information to dynamic content PROFILER_SCRIPT_TYPE_DYNAMIC |
| funcBody->SetIsTopLevel(true); |
| |
| // If not library code then let's find the parent, we may need to register this source if any exception happens later |
| if ((grfscr & fscrIsLibraryCode) == 0) |
| { |
| // For parented eval get the caller's utf8SourceInfo |
| JavascriptFunction* pfuncCaller; |
| if (JavascriptStackWalker::GetCaller(&pfuncCaller, scriptContext) && pfuncCaller && pfuncCaller->IsScriptFunction()) |
| { |
| FunctionBody* parentFuncBody = pfuncCaller->GetFunctionBody(); |
| Utf8SourceInfo* parentUtf8SourceInfo = parentFuncBody->GetUtf8SourceInfo(); |
| Utf8SourceInfo* utf8SourceInfo = funcBody->GetUtf8SourceInfo(); |
| utf8SourceInfo->SetCallerUtf8SourceInfo(parentUtf8SourceInfo); |
| } |
| } |
| |
| if (registerDocument) |
| { |
| funcBody->RegisterFuncToDiag(scriptContext, pszTitle); |
| funcBody = funcBody->GetParseableFunctionInfo(); // RegisterFunction may parse and update function body |
| } |
| |
| ScriptFunction* pfuncScript = funcBody->IsCoroutine() ? |
| scriptContext->GetLibrary()->CreateGeneratorVirtualScriptFunction(funcBody) : |
| scriptContext->GetLibrary()->CreateScriptFunction(funcBody); |
| |
| return pfuncScript; |
| } |
| } |
| |
| #ifdef IR_VIEWER |
| Var GlobalObject::IRDumpEvalHelper(ScriptContext* scriptContext, const char16 *source, |
| int sourceLength, ModuleID moduleID, uint32 grfscr, LPCOLESTR pszTitle, |
| BOOL registerDocument, BOOL isIndirect, BOOL strictMode) |
| { |
| // TODO (t-doilij) clean up this function, specifically used for IR dump (don't execute bytecode; potentially dangerous) |
| |
| Assert(sourceLength >= 0); |
| |
| if (scriptContext->GetThreadContext()->EvalDisabled()) |
| { |
| throw Js::EvalDisabledException(); |
| } |
| |
| #ifdef PROFILE_EXEC |
| scriptContext->ProfileBegin(Js::EvalCompilePhase); |
| #endif |
| void * frameAddr = nullptr; |
| GET_CURRENT_FRAME_ID(frameAddr); |
| |
| HRESULT hr = S_OK; |
| HRESULT hrParser = S_OK; |
| HRESULT hrCodeGen = S_OK; |
| CompileScriptException se; |
| Js::ParseableFunctionInfo * funcBody = NULL; |
| |
| BEGIN_LEAVE_SCRIPT_INTERNAL(scriptContext); |
| BEGIN_TRANSLATE_EXCEPTION_TO_HRESULT |
| { |
| size_t cchSource = sourceLength; |
| size_t cbUtf8Buffer = (cchSource + 1) * 3; |
| |
| ArenaAllocator tempArena(_u("EvalHelperArena"), scriptContext->GetThreadContext()->GetPageAllocator(), Js::Throw::OutOfMemory); |
| LPUTF8 utf8Source = AnewArray(&tempArena, utf8char_t, cbUtf8Buffer); |
| |
| Assert(cchSource < MAXLONG); |
| size_t cbSource = utf8::EncodeIntoAndNullTerminate(utf8Source, source, static_cast< charcount_t >(cchSource)); |
| Assert(cbSource + 1 <= cbUtf8Buffer); |
| |
| SRCINFO const * pSrcInfo = scriptContext->GetModuleSrcInfo(moduleID); |
| // Source Info objects are kept alive by the function bodies that are referencing it |
| // The function body is created in GenerateByteCode but the source info isn't passed in, only the index |
| // So we need to pin it here (TODO: Change GenerateByteCode to take in the sourceInfo itself) |
| ENTER_PINNED_SCOPE(Utf8SourceInfo, sourceInfo); |
| sourceInfo = Utf8SourceInfo::New(scriptContext, utf8Source, cchSource, cbSource, pSrcInfo, ((grfscr & fscrIsLibraryCode) != 0)); |
| |
| Parser parser(scriptContext, strictMode); |
| bool forceNoNative = false; |
| |
| ParseNodePtr parseTree; |
| |
| SourceContextInfo * sourceContextInfo = pSrcInfo->sourceContextInfo; |
| |
| grfscr = grfscr | fscrDynamicCode; |
| |
| hrParser = parser.ParseCesu8Source(&parseTree, utf8Source, cbSource, grfscr, &se, &sourceContextInfo->nextLocalFunctionId, |
| sourceContextInfo); |
| sourceInfo->SetParseFlags(grfscr); |
| |
| if (SUCCEEDED(hrParser) && parseTree) |
| { |
| // This keeps function bodies generated by the byte code alive till we return |
| Js::AutoDynamicCodeReference dynamicFunctionReference(scriptContext); |
| |
| Assert(cchSource < MAXLONG); |
| uint sourceIndex = scriptContext->SaveSourceNoCopy(sourceInfo, cchSource, true); |
| |
| grfscr = grfscr | fscrIrDumpEnable; |
| |
| hrCodeGen = GenerateByteCode(parseTree, grfscr, scriptContext, &funcBody, sourceIndex, forceNoNative, &parser, &se); |
| sourceInfo->SetByteCodeGenerationFlags(grfscr); |
| } |
| |
| LEAVE_PINNED_SCOPE(); |
| } |
| END_TRANSLATE_EXCEPTION_TO_HRESULT(hr); |
| END_LEAVE_SCRIPT_INTERNAL(scriptContext); |
| |
| |
| #ifdef PROFILE_EXEC |
| scriptContext->ProfileEnd(Js::EvalCompilePhase); |
| #endif |
| THROW_KNOWN_HRESULT_EXCEPTIONS(hr); |
| |
| if (!SUCCEEDED(hrParser)) |
| { |
| hrParser = SCRIPT_E_RECORDED; |
| EXCEPINFO ei; |
| se.GetError(&hrParser, &ei); |
| |
| ErrorTypeEnum errorType; |
| switch ((HRESULT)ei.scode) |
| { |
| #define RT_ERROR_MSG(name, errnum, str1, str2, jst, errorNumSource) \ |
| case name: \ |
| errorType = jst; \ |
| break; |
| #define RT_PUBLICERROR_MSG(name, errnum, str1, str2, jst, errorNumSource) RT_ERROR_MSG(name, errnum, str1, str2, jst, errorNumSource) |
| #include "rterrors.h" |
| #undef RT_PUBLICERROR_MSG |
| #undef RT_ERROR_MSG |
| default: |
| errorType = kjstSyntaxError; |
| } |
| JavascriptError::MapAndThrowError(scriptContext, ei.scode, errorType, &ei); |
| } |
| else if (!SUCCEEDED(hrCodeGen)) |
| { |
| Assert(hrCodeGen == SCRIPT_E_RECORDED); |
| hrCodeGen = se.ei.scode; |
| /* |
| * VBSERR_OutOfStack is of type kjstError but we throw a (more specific) StackOverflowError when a hard stack |
| * overflow occurs. To keep the behavior consistent I'm special casing it here. |
| */ |
| se.Free(); |
| if (hrCodeGen == VBSERR_OutOfStack) |
| { |
| JavascriptError::ThrowStackOverflowError(scriptContext); |
| } |
| JavascriptError::MapAndThrowError(scriptContext, hrCodeGen); |
| } |
| else |
| { |
| Assert(funcBody != nullptr); |
| funcBody->SetDisplayName(pszTitle); |
| |
| // Set the functionbody information to dynamic content PROFILER_SCRIPT_TYPE_DYNAMIC |
| funcBody->SetIsTopLevel(true); |
| |
| if (registerDocument) |
| { |
| funcBody->RegisterFuncToDiag(scriptContext, pszTitle); |
| funcBody = funcBody->GetParseableFunctionInfo(); // RegisterFunction may parse and update function body |
| } |
| |
| NativeCodeGenerator *nativeCodeGenerator = scriptContext->GetNativeCodeGenerator(); |
| Js::FunctionBody *functionBody = funcBody->GetFunctionBody(); // funcBody is ParseableFunctionInfo |
| |
| if (!functionBody || !functionBody->GetByteCode()) |
| { |
| return scriptContext->GetLibrary()->GetUndefined(); |
| } |
| |
| Js::Var retVal = RejitIRViewerFunction(nativeCodeGenerator, functionBody, scriptContext); |
| return retVal; |
| } |
| } |
| #endif /* IR_VIEWER */ |
| |
| Var GlobalObject::EntryParseInt(RecyclableObject* function, CallInfo callInfo, ...) |
| { |
| PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault); |
| |
| ARGUMENTS(args, callInfo); |
| ScriptContext* scriptContext = function->GetScriptContext(); |
| |
| Assert(!(callInfo.Flags & CallFlags_New)); |
| |
| AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'"); |
| |
| JavascriptString *str; |
| int radix = 0; |
| |
| |
| if(args.Info.Count < 2) |
| { |
| // We're really converting "undefined" here, but "undefined" produces |
| // NaN, so just return that directly. |
| return JavascriptNumber::ToVarNoCheck(JavascriptNumber::NaN, scriptContext); |
| } |
| |
| |
| // Short cut for numbers and radix 10 |
| if (((args.Info.Count == 2) || |
| (TaggedInt::Is(args[2]) && TaggedInt::ToInt32(args[2]) == 10) || |
| (JavascriptNumber::Is(args[2]) && JavascriptNumber::GetValue(args[2]) == 10))) |
| { |
| if (TaggedInt::Is(args[1])) |
| { |
| return args[1]; |
| } |
| if (JavascriptNumber::Is(args[1])) |
| { |
| double value = JavascriptNumber::GetValue(args[1]); |
| |
| // make sure we are in the ranges that don't have exponential notation. |
| double absValue = Math::Abs(value); |
| if (absValue < 1.0e21 && absValue >= 1e-5) |
| { |
| double result; |
| #pragma prefast(suppress:6031, "We don't care about the fraction part") |
| ::modf(value, &result); |
| return JavascriptNumber::ToVarIntCheck(result, scriptContext); |
| } |
| } |
| } |
| |
| // convert input to a string |
| if (JavascriptString::Is(args[1])) |
| { |
| str = JavascriptString::FromVar(args[1]); |
| } |
| else |
| { |
| str = JavascriptConversion::ToString(args[1], scriptContext); |
| } |
| |
| if(args.Info.Count > 2) |
| { |
| // retrieve the radix |
| // TODO : verify for ES5 : radix is 10 when undefined or 0, except when it starts with 0x when it is 16. |
| radix = JavascriptConversion::ToInt32(args[2], scriptContext); |
| if(radix < 0 || radix == 1 || radix > 36) |
| { |
| //illegal, return NaN |
| return JavascriptNumber::ToVarNoCheck(JavascriptNumber::NaN, scriptContext); |
| } |
| } |
| |
| Var result = str->ToInteger(radix); |
| return result; |
| } |
| |
| Var GlobalObject::EntryParseFloat(RecyclableObject* function, CallInfo callInfo, ...) |
| { |
| PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault); |
| |
| ARGUMENTS(args, callInfo); |
| ScriptContext* scriptContext = function->GetScriptContext(); |
| |
| Assert(!(callInfo.Flags & CallFlags_New)); |
| |
| AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'"); |
| |
| JavascriptString *str; |
| |
| if(args.Info.Count < 2) |
| { |
| // We're really converting "undefined" here, but "undefined" produces |
| // NaN, so just return that directly. |
| return JavascriptNumber::ToVarNoCheck(JavascriptNumber::NaN, scriptContext); |
| } |
| |
| // Short cut for numbers |
| if (TaggedInt::Is(args[1])) |
| { |
| return args[1]; |
| } |
| if(JavascriptNumber::Is_NoTaggedIntCheck(args[1])) |
| { |
| // If the value is negative zero, return positive zero. Since the parameter type is string, -0 would first be |
| // converted to the string "0", then parsed to result in positive zero. |
| return |
| JavascriptNumber::IsNegZero(JavascriptNumber::GetValue(args[1])) ? |
| TaggedInt::ToVarUnchecked(0) : |
| args[1]; |
| } |
| |
| // convert input to a string |
| if (JavascriptString::Is(args[1])) |
| { |
| str = JavascriptString::FromVar(args[1]); |
| } |
| else |
| { |
| str = JavascriptConversion::ToString(args[1], scriptContext); |
| } |
| |
| // skip preceding whitespace |
| const char16 *pch = scriptContext->GetCharClassifier()->SkipWhiteSpace(str->GetSz()); |
| |
| // perform the string -> float conversion |
| double result = NumberUtilities::StrToDbl(pch, &pch, scriptContext); |
| |
| return JavascriptNumber::ToVarNoCheck(result, scriptContext); |
| } |
| |
| Var GlobalObject::EntryIsNaN(RecyclableObject* function, CallInfo callInfo, ...) |
| { |
| PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault); |
| |
| ARGUMENTS(args, callInfo); |
| ScriptContext* scriptContext = function->GetScriptContext(); |
| |
| Assert(!(callInfo.Flags & CallFlags_New)); |
| |
| AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'"); |
| |
| if(args.Info.Count < 2) |
| { |
| return scriptContext->GetLibrary()->GetTrue(); |
| } |
| return JavascriptBoolean::ToVar( |
| JavascriptNumber::IsNan(JavascriptConversion::ToNumber(args[1],scriptContext)), |
| scriptContext); |
| } |
| |
| Var GlobalObject::EntryIsFinite(RecyclableObject* function, CallInfo callInfo, ...) |
| { |
| PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault); |
| |
| ARGUMENTS(args, callInfo); |
| ScriptContext* scriptContext = function->GetScriptContext(); |
| |
| Assert(!(callInfo.Flags & CallFlags_New)); |
| |
| AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'"); |
| if(args.Info.Count < 2) |
| { |
| return scriptContext->GetLibrary()->GetFalse(); |
| } |
| return JavascriptBoolean::ToVar( |
| NumberUtilities::IsFinite(JavascriptConversion::ToNumber(args[1],scriptContext)), |
| scriptContext); |
| } |
| |
| Var GlobalObject::EntryDecodeURI(RecyclableObject* function, CallInfo callInfo, ...) |
| { |
| PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault); |
| |
| ARGUMENTS(args, callInfo); |
| ScriptContext* scriptContext = function->GetScriptContext(); |
| |
| Assert(!(callInfo.Flags & CallFlags_New)); |
| |
| AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'"); |
| return UriHelper::DecodeCoreURI(scriptContext, args, UriHelper::URIReserved); |
| } |
| |
| Var GlobalObject::EntryDecodeURIComponent(RecyclableObject* function, CallInfo callInfo, ...) |
| { |
| PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault); |
| |
| ARGUMENTS(args, callInfo); |
| ScriptContext* scriptContext = function->GetScriptContext(); |
| |
| Assert(!(callInfo.Flags & CallFlags_New)); |
| |
| AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'"); |
| return UriHelper::DecodeCoreURI(scriptContext, args, UriHelper::URINone); |
| } |
| |
| Var GlobalObject::EntryEncodeURI(RecyclableObject* function, CallInfo callInfo, ...) |
| { |
| PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault); |
| |
| ARGUMENTS(args, callInfo); |
| ScriptContext* scriptContext = function->GetScriptContext(); |
| |
| Assert(!(callInfo.Flags & CallFlags_New)); |
| |
| AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'"); |
| return UriHelper::EncodeCoreURI(scriptContext, args, UriHelper::URIReserved | UriHelper::URIUnescaped); |
| } |
| |
| Var GlobalObject::EntryEncodeURIComponent(RecyclableObject* function, CallInfo callInfo, ...) |
| { |
| PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault); |
| |
| ARGUMENTS(args, callInfo); |
| ScriptContext* scriptContext = function->GetScriptContext(); |
| |
| Assert(!(callInfo.Flags & CallFlags_New)); |
| |
| AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'"); |
| |
| return UriHelper::EncodeCoreURI(scriptContext, args, UriHelper::URIUnescaped); |
| } |
| |
| // |
| // Part of Appendix B of ES5 spec |
| // |
| |
| // This is a bit field for the hex values: 00-29, 2C, 3A-3F, 5B-5E, 60, 7B-FF |
| // These are the values escape encodes using the default mask (or mask >= 4) |
| static const BYTE _grfbitEscape[] = |
| { |
| 0xFF, 0xFF, // 00 - 0F |
| 0xFF, 0xFF, // 10 - 1F |
| 0xFF, 0x13, // 20 - 2F |
| 0x00, 0xFC, // 30 - 3F |
| 0x00, 0x00, // 40 - 4F |
| 0x00, 0x78, // 50 - 5F |
| 0x01, 0x00, // 60 - 6F |
| 0x00, 0xF8, // 70 - 7F |
| 0xFF, 0xFF, // 80 - 8F |
| 0xFF, 0xFF, // 90 - 9F |
| 0xFF, 0xFF, // A0 - AF |
| 0xFF, 0xFF, // B0 - BF |
| 0xFF, 0xFF, // C0 - CF |
| 0xFF, 0xFF, // D0 - DF |
| 0xFF, 0xFF, // E0 - EF |
| 0xFF, 0xFF, // F0 - FF |
| }; |
| Var GlobalObject::EntryEscape(RecyclableObject* function, CallInfo callInfo, ...) |
| { |
| PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault); |
| |
| ARGUMENTS(args, callInfo); |
| ScriptContext* scriptContext = function->GetScriptContext(); |
| |
| Assert(!(callInfo.Flags & CallFlags_New)); |
| |
| if (args.Info.Count <= 1) |
| { |
| return scriptContext->GetLibrary()->GetUndefinedDisplayString(); |
| } |
| |
| JavascriptString *src = JavascriptConversion::ToString(args[1], scriptContext); |
| |
| CompoundString *const bs = CompoundString::NewWithCharCapacity(src->GetLength(), scriptContext->GetLibrary()); |
| char16 chw; |
| char16 * pchSrc; |
| char16 * pchLim; |
| |
| const char _rgchHex[] = "0123456789ABCDEF"; |
| |
| pchSrc = const_cast<char16 *>(src->GetString()); |
| pchLim = pchSrc + src->GetLength(); |
| while (pchSrc < pchLim) |
| { |
| chw = *pchSrc++; |
| |
| if (0 != (chw & 0xFF00)) |
| { |
| bs->AppendChars(_u("%u")); |
| bs->AppendChars(static_cast<char16>(_rgchHex[(chw >> 12) & 0x0F])); |
| bs->AppendChars(static_cast<char16>(_rgchHex[(chw >> 8) & 0x0F])); |
| bs->AppendChars(static_cast<char16>(_rgchHex[(chw >> 4) & 0x0F])); |
| bs->AppendChars(static_cast<char16>(_rgchHex[chw & 0x0F])); |
| } |
| else if (_grfbitEscape[chw >> 3] & (1 << (chw & 7))) |
| { |
| // Escape the byte. |
| bs->AppendChars(_u('%')); |
| bs->AppendChars(static_cast<char16>(_rgchHex[chw >> 4])); |
| bs->AppendChars(static_cast<char16>(_rgchHex[chw & 0x0F])); |
| } |
| else |
| { |
| bs->AppendChars(chw); |
| } |
| } |
| |
| return bs; |
| } |
| |
| // |
| // Part of Appendix B of ES5 spec |
| // |
| Var GlobalObject::EntryUnEscape(RecyclableObject* function, CallInfo callInfo, ...) |
| { |
| PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault); |
| |
| ARGUMENTS(args, callInfo); |
| ScriptContext* scriptContext = function->GetScriptContext(); |
| |
| Assert(!(callInfo.Flags & CallFlags_New)); |
| |
| if (args.Info.Count <= 1) |
| { |
| return scriptContext->GetLibrary()->GetUndefinedDisplayString(); |
| } |
| |
| char16 chw; |
| char16 chT; |
| char16 * pchSrc; |
| char16 * pchLim; |
| char16 * pchMin; |
| |
| JavascriptString *src = JavascriptConversion::ToString(args[1], scriptContext); |
| |
| CompoundString *const bs = CompoundString::NewWithCharCapacity(src->GetLength(), scriptContext->GetLibrary()); |
| |
| pchSrc = const_cast<char16 *>(src->GetString()); |
| pchLim = pchSrc + src->GetLength(); |
| while (pchSrc < pchLim) |
| { |
| if ('%' != (chT = *pchSrc++)) |
| { |
| bs->AppendChars(chT); |
| continue; |
| } |
| |
| pchMin = pchSrc; |
| chT = *pchSrc++; |
| if ('u' != chT) |
| { |
| chw = 0; |
| goto LTwoHexDigits; |
| } |
| |
| // 4 hex digits. |
| if ((chT = *pchSrc++ - '0') <= 9) |
| chw = chT * 0x1000; |
| else if ((chT -= 'A' - '0') <= 5 || (chT -= 'a' - 'A') <= 5) |
| chw = (chT + 10) * 0x1000; |
| else |
| goto LHexError; |
| if ((chT = *pchSrc++ - '0') <= 9) |
| chw += chT * 0x0100; |
| else if ((chT -= 'A' - '0') <= 5 || (chT -= 'a' - 'A') <= 5) |
| chw += (chT + 10) * 0x0100; |
| else |
| goto LHexError; |
| chT = *pchSrc++; |
| LTwoHexDigits: |
| if ((chT -= '0') <= 9) |
| chw += chT * 0x0010; |
| else if ((chT -= 'A' - '0') <= 5 || (chT -= 'a' - 'A') <= 5) |
| chw += (chT + 10) * 0x0010; |
| else |
| goto LHexError; |
| if ((chT = *pchSrc++ - '0') <= 9) |
| chw += chT; |
| else if ((chT -= 'A' - '0') <= 5 || (chT -= 'a' - 'A') <= 5) |
| chw += chT + 10; |
| else |
| { |
| LHexError: |
| pchSrc = pchMin; |
| chw = '%'; |
| } |
| |
| bs->AppendChars(chw); |
| } |
| |
| return bs; |
| } |
| |
| #if DBG |
| void DebugClearStack() |
| { |
| char buffer[1024]; |
| memset(buffer, 0, sizeof(buffer)); |
| } |
| #endif |
| |
| Var GlobalObject::EntryCollectGarbage(RecyclableObject* function, CallInfo callInfo, ...) |
| { |
| PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault); |
| |
| #if DBG_DUMP |
| if (Js::Configuration::Global.flags.TraceWin8Allocations) |
| { |
| Output::Print(_u("MemoryTrace: GlobalObject::EntryCollectGarbage Invoke\n")); |
| Output::Flush(); |
| } |
| #endif |
| |
| #if DBG |
| // Clear 1K of stack to avoid false positive in debug build. |
| // Because we don't debug build don't stack pack |
| DebugClearStack(); |
| #endif |
| Assert(!(callInfo.Flags & CallFlags_New)); |
| |
| ScriptContext* scriptContext = function->GetScriptContext(); |
| if (!scriptContext->GetConfig()->IsCollectGarbageEnabled() |
| #ifdef ENABLE_PROJECTION |
| && scriptContext->GetConfig()->GetHostType() != HostType::HostTypeApplication |
| && scriptContext->GetConfig()->GetHostType() != HostType::HostTypeWebview |
| #endif |
| ) |
| { |
| // We expose the CollectGarbage API with flag for compat reasons. |
| // If CollectGarbage key is not enabled, and if the HostType is neither |
| // HostType::HostTypeApplication nor HostType::HostTypeWebview, |
| // then we do not trigger collection. |
| return scriptContext->GetLibrary()->GetUndefined(); |
| } |
| |
| Recycler* recycler = scriptContext->GetRecycler(); |
| if (recycler) |
| { |
| #if DBG && defined(RECYCLER_DUMP_OBJECT_GRAPH) |
| ARGUMENTS(args, callInfo); |
| if (args.Info.Count > 1) |
| { |
| double value = JavascriptConversion::ToNumber(args[1], function->GetScriptContext()); |
| if (value == 1.0) |
| { |
| recycler->dumpObjectOnceOnCollect = true; |
| } |
| } |
| #endif |
| recycler->CollectNow<CollectNowDecommitNowExplicit>(); |
| } |
| |
| #if DBG_DUMP |
| #ifdef ENABLE_PROJECTION |
| scriptContext->GetThreadContext()->DumpProjectionContextMemoryStats(_u("Stats after GlobalObject::EntryCollectGarbage call")); |
| #endif |
| |
| if (Js::Configuration::Global.flags.TraceWin8Allocations) |
| { |
| Output::Print(_u("MemoryTrace: GlobalObject::EntryCollectGarbage Exit\n")); |
| Output::Flush(); |
| } |
| #endif |
| |
| return scriptContext->GetLibrary()->GetUndefined(); |
| } |
| |
| #ifdef ENABLE_DEBUG_CONFIG_OPTIONS |
| Var GlobalObject::EntryChWriteTraceEvent(RecyclableObject *function, CallInfo callInfo, ...) |
| { |
| PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault); |
| ARGUMENTS(args, callInfo); |
| |
| if (args.Info.Count < 2) |
| { |
| return function->GetScriptContext()->GetLibrary()->GetUndefined(); |
| } |
| |
| Js::JavascriptString* jsString = Js::JavascriptConversion::ToString(args[1], function->GetScriptContext()); |
| PlatformAgnostic::EventTrace::FireGenericEventTrace(jsString->GetSz()); |
| return function->GetScriptContext()->GetLibrary()->GetUndefined(); |
| } |
| #endif |
| |
| #if ENABLE_TTD |
| //Log a string in the telemetry system (and print to the console) |
| Var GlobalObject::EntryTelemetryLog(RecyclableObject* function, CallInfo callInfo, ...) |
| { |
| PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault); |
| ARGUMENTS(args, callInfo); |
| |
| TTDAssert(args.Info.Count >= 2 && Js::JavascriptString::Is(args[1]), "Bad arguments!!!"); |
| |
| Js::JavascriptString* jsString = Js::JavascriptString::FromVar(args[1]); |
| bool doPrint = (args.Info.Count == 3) && Js::JavascriptBoolean::Is(args[2]) && (Js::JavascriptBoolean::FromVar(args[2])->GetValue()); |
| |
| if(function->GetScriptContext()->ShouldPerformReplayAction()) |
| { |
| function->GetScriptContext()->GetThreadContext()->TTDLog->ReplayTelemetryLogEvent(jsString); |
| } |
| |
| if(function->GetScriptContext()->ShouldPerformRecordAction()) |
| { |
| function->GetScriptContext()->GetThreadContext()->TTDLog->RecordTelemetryLogEvent(jsString, doPrint); |
| } |
| |
| if(doPrint) |
| { |
| // |
| //TODO: the host should give us a print callback which we can use here |
| // |
| wprintf(_u("%ls\n"), jsString->GetSz()); |
| fflush(stdout); |
| } |
| |
| return function->GetScriptContext()->GetLibrary()->GetUndefined(); |
| } |
| |
| //Check if diagnostic trace writing is enabled |
| Var GlobalObject::EntryEnabledDiagnosticsTrace(RecyclableObject* function, CallInfo callInfo, ...) |
| { |
| PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault); |
| ARGUMENTS(args, callInfo); |
| |
| if(function->GetScriptContext()->ShouldPerformRecordOrReplayAction()) |
| { |
| return function->GetScriptContext()->GetLibrary()->GetTrue(); |
| } |
| else |
| { |
| return function->GetScriptContext()->GetLibrary()->GetFalse(); |
| } |
| } |
| |
| //Write a copy of the current TTD log to a specified location |
| Var GlobalObject::EntryEmitTTDLog(RecyclableObject* function, CallInfo callInfo, ...) |
| { |
| PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault); |
| ARGUMENTS(args, callInfo); |
| |
| Js::JavascriptLibrary* jslib = function->GetScriptContext()->GetLibrary(); |
| |
| if(args.Info.Count != 2 || !Js::JavascriptString::Is(args[1])) |
| { |
| return jslib->GetFalse(); |
| } |
| |
| if(function->GetScriptContext()->ShouldPerformReplayAction()) |
| { |
| function->GetScriptContext()->GetThreadContext()->TTDLog->ReplayEmitLogEvent(); |
| |
| return jslib->GetTrue(); |
| } |
| |
| if(function->GetScriptContext()->ShouldPerformRecordAction()) |
| { |
| Js::JavascriptString* jsString = Js::JavascriptString::FromVar(args[1]); |
| function->GetScriptContext()->GetThreadContext()->TTDLog->RecordEmitLogEvent(jsString); |
| |
| return jslib->GetTrue(); |
| } |
| |
| return jslib->GetFalse(); |
| } |
| #endif |
| |
| //Pattern match is unique to RuntimeObject. Only leading and trailing * are implemented |
| //Example: *pat*tern* actually matches all the strings having pat*tern as substring. |
| BOOL GlobalObject::MatchPatternHelper(JavascriptString *propertyName, JavascriptString *pattern, ScriptContext *scriptContext) |
| { |
| if (nullptr == propertyName || nullptr == pattern) |
| { |
| return FALSE; |
| } |
| |
| charcount_t patternLength = pattern->GetLength(); |
| charcount_t nameLength = propertyName->GetLength(); |
| BOOL bStart; |
| BOOL bEnd; |
| |
| if (0 == patternLength) |
| { |
| return TRUE; // empty pattern matches all |
| } |
| |
| bStart = (_u('*') == pattern->GetItem(0));// Is there a start star? |
| bEnd = (_u('*') == pattern->GetItem(patternLength - 1));// Is there an end star? |
| |
| //deal with the stars |
| if (bStart || bEnd) |
| { |
| JavascriptString *subPattern; |
| int idxStart = bStart? 1: 0; |
| int idxEnd = bEnd? (patternLength - 1): patternLength; |
| if (idxEnd <= idxStart) |
| { |
| return TRUE; //scenario double star ** |
| } |
| |
| //get a pattern which doesn't contain leading and trailing stars |
| subPattern = JavascriptString::FromVar(JavascriptString::SubstringCore(pattern, idxStart, idxEnd - idxStart, scriptContext)); |
| |
| uint index = JavascriptString::strstr(propertyName, subPattern, false); |
| |
| if (index == (uint)-1) |
| { |
| return FALSE; |
| } |
| if (FALSE == bStart) |
| { |
| return index == 0; //begin at the same place; |
| } |
| else if (FALSE == bEnd) |
| { |
| return (index + patternLength - 1) == (nameLength); |
| } |
| return TRUE; |
| } |
| else |
| { |
| //full match |
| if (0 == JavascriptString::strcmp(propertyName, pattern)) |
| { |
| return TRUE; |
| } |
| } |
| |
| return FALSE; |
| } |
| |
| PropertyQueryFlags GlobalObject::HasPropertyQuery(PropertyId propertyId) |
| { |
| return JavascriptConversion::BooleanToPropertyQueryFlags(JavascriptConversion::PropertyQueryFlagsToBoolean(DynamicObject::HasPropertyQuery(propertyId)) || |
| (this->directHostObject && JavascriptOperators::HasProperty(this->directHostObject, propertyId)) || |
| (this->hostObject && JavascriptOperators::HasProperty(this->hostObject, propertyId))); |
| } |
| |
| BOOL GlobalObject::HasRootProperty(PropertyId propertyId) |
| { |
| return __super::HasRootProperty(propertyId) || |
| (this->directHostObject && JavascriptOperators::HasProperty(this->directHostObject, propertyId)) || |
| (this->hostObject && JavascriptOperators::HasProperty(this->hostObject, propertyId)); |
| } |
| |
| BOOL GlobalObject::HasOwnProperty(PropertyId propertyId) |
| { |
| return JavascriptConversion::PropertyQueryFlagsToBoolean(DynamicObject::HasPropertyQuery(propertyId)) || |
| (this->directHostObject && this->directHostObject->HasProperty(propertyId)); |
| } |
| |
| BOOL GlobalObject::HasOwnPropertyNoHostObject(PropertyId propertyId) |
| { |
| return JavascriptConversion::PropertyQueryFlagsToBoolean(DynamicObject::HasPropertyQuery(propertyId)); |
| } |
| |
| PropertyQueryFlags GlobalObject::GetPropertyQuery(Var originalInstance, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext) |
| { |
| if (JavascriptConversion::PropertyQueryFlagsToBoolean(DynamicObject::GetPropertyQuery(originalInstance, propertyId, value, info, requestContext))) |
| { |
| return Property_Found; |
| } |
| return JavascriptConversion::BooleanToPropertyQueryFlags((this->directHostObject && JavascriptOperators::GetProperty(this->directHostObject, propertyId, value, requestContext, info)) || |
| (this->hostObject && JavascriptOperators::GetProperty(this->hostObject, propertyId, value, requestContext, info))); |
| } |
| |
| PropertyQueryFlags GlobalObject::GetPropertyQuery(Var originalInstance, JavascriptString* propertyNameString, Var* value, PropertyValueInfo* info, ScriptContext* requestContext) |
| { |
| PropertyRecord const* propertyRecord; |
| this->GetScriptContext()->GetOrAddPropertyRecord(propertyNameString->GetString(), propertyNameString->GetLength(), &propertyRecord); |
| return GlobalObject::GetPropertyQuery(originalInstance, propertyRecord->GetPropertyId(), value, info, requestContext); |
| } |
| |
| BOOL GlobalObject::GetRootProperty(Var originalInstance, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext) |
| { |
| if (__super::GetRootProperty(originalInstance, propertyId, value, info, requestContext)) |
| { |
| return TRUE; |
| } |
| return (this->directHostObject && JavascriptOperators::GetProperty(this->directHostObject, propertyId, value, requestContext, info)) || |
| (this->hostObject && JavascriptOperators::GetProperty(this->hostObject, propertyId, value, requestContext, info)); |
| } |
| |
| BOOL GlobalObject::GetAccessors(PropertyId propertyId, Var* getter, Var* setter, ScriptContext * requestContext) |
| { |
| if (DynamicObject::GetAccessors(propertyId, getter, setter, requestContext)) |
| { |
| return TRUE; |
| } |
| if (this->directHostObject) |
| { |
| return this->directHostObject->GetAccessors(propertyId, getter, setter, requestContext); |
| } |
| else if (this->hostObject) |
| { |
| return this->hostObject->GetAccessors(propertyId, getter, setter, requestContext); |
| } |
| return FALSE; |
| } |
| |
| PropertyQueryFlags GlobalObject::GetPropertyReferenceQuery(Var originalInstance, PropertyId propertyId, Var* value, PropertyValueInfo* info, |
| ScriptContext* requestContext) |
| { |
| if (JavascriptConversion::PropertyQueryFlagsToBoolean(DynamicObject::GetPropertyReferenceQuery(originalInstance, propertyId, value, info, requestContext))) |
| { |
| return Property_Found; |
| } |
| return JavascriptConversion::BooleanToPropertyQueryFlags((this->directHostObject && JavascriptOperators::GetPropertyReference(this->directHostObject, propertyId, value, requestContext, info)) || |
| (this->hostObject && JavascriptOperators::GetPropertyReference(this->hostObject, propertyId, value, requestContext, info))); |
| } |
| |
| BOOL GlobalObject::GetRootPropertyReference(Var originalInstance, PropertyId propertyId, Var* value, PropertyValueInfo* info, |
| ScriptContext* requestContext) |
| { |
| if (__super::GetRootPropertyReference(originalInstance, propertyId, value, info, requestContext)) |
| { |
| return true; |
| } |
| return (this->directHostObject && JavascriptOperators::GetPropertyReference(this->directHostObject, propertyId, value, requestContext, info)) || |
| (this->hostObject && JavascriptOperators::GetPropertyReference(this->hostObject, propertyId, value, requestContext, info)); |
| } |
| |
| BOOL GlobalObject::InitProperty(PropertyId propertyId, Var value, PropertyOperationFlags flags, PropertyValueInfo* info) |
| { |
| // var x = 10; variables declared with "var" at global scope |
| // These cannot be deleted |
| // In ES5 they are enumerable. |
| |
| PropertyAttributes attributes = PropertyWritable | PropertyEnumerable | PropertyDeclaredGlobal; |
| flags = static_cast<PropertyOperationFlags>(flags | PropertyOperation_ThrowIfNotExtensible); |
| return DynamicObject::SetPropertyWithAttributes(propertyId, value, attributes, info, flags); |
| } |
| |
| BOOL GlobalObject::InitPropertyScoped(PropertyId propertyId, Var value) |
| { |
| // var x = 10; variables declared with "var" inside "eval" |
| // These CAN be deleted |
| // in ES5 they are enumerable. |
| // |
| PropertyAttributes attributes = PropertyDynamicTypeDefaults | PropertyDeclaredGlobal; |
| return DynamicObject::SetPropertyWithAttributes(propertyId, value, attributes, NULL, PropertyOperation_ThrowIfNotExtensible); |
| } |
| |
| BOOL GlobalObject::InitFuncScoped(PropertyId propertyId, Var value) |
| { |
| // Var binding of functions declared in eval are elided when conflicting |
| // with global scope let/const variables, so do not actually set the |
| // property if it exists and is a let/const variable. |
| bool noRedecl = false; |
| if (!GetTypeHandler()->HasRootProperty(this, propertyId, &noRedecl) || !noRedecl) |
| { |
| // |
| // var x = 10; variables declared with "var" inside "eval" |
| // These CAN be deleted |
| // in ES5 they are enumerable. |
| |
| PropertyAttributes attributes = PropertyDynamicTypeDefaults; |
| |
| DynamicObject::SetPropertyWithAttributes(propertyId, value, attributes, NULL, PropertyOperation_ThrowIfNotExtensible); |
| } |
| return true; |
| } |
| |
| BOOL GlobalObject::SetExistingProperty(PropertyId propertyId, Var value, PropertyValueInfo* info, BOOL *setAttempted) |
| { |
| BOOL hasOwnProperty = JavascriptConversion::PropertyQueryFlagsToBoolean(DynamicObject::HasPropertyQuery(propertyId)); |
| BOOL hasProperty = JavascriptOperators::HasProperty(this->GetPrototype(), propertyId); |
| *setAttempted = TRUE; |
| |
| if (this->directHostObject && |
| !hasOwnProperty && |
| !hasProperty && |
| this->directHostObject->HasProperty(propertyId)) |
| { |
| // directHostObject->HasProperty returns true for things in global collections, however, they are not able to be set. |
| // When we call SetProperty we'll return FALSE which means fall back to GlobalObject::SetProperty and shadow our collection |
| // object rather than failing the set altogether. See bug Windows Out Of Band Releases 1144780 and linked bugs for details. |
| if (this->directHostObject->SetProperty(propertyId, value, PropertyOperation_None, info)) |
| { |
| return TRUE; |
| } |
| } |
| else |
| if (this->hostObject && |
| // Consider to revert to the commented line and ignore the prototype chain when direct host is on by default in IE9 mode |
| !hasOwnProperty && |
| !hasProperty && |
| this->hostObject->HasProperty(propertyId)) |
| { |
| return this->hostObject->SetProperty(propertyId, value, PropertyOperation_None, NULL); |
| } |
| |
| if (hasOwnProperty || hasProperty) |
| { |
| return DynamicObject::SetProperty(propertyId, value, PropertyOperation_None, info); |
| } |
| |
| *setAttempted = FALSE; |
| return FALSE; |
| } |
| |
| BOOL GlobalObject::SetExistingRootProperty(PropertyId propertyId, Var value, PropertyValueInfo* info, BOOL *setAttempted) |
| { |
| BOOL hasOwnProperty = __super::HasRootProperty(propertyId); |
| BOOL hasProperty = JavascriptOperators::HasProperty(this->GetPrototype(), propertyId); |
| *setAttempted = TRUE; |
| |
| if (this->directHostObject && |
| !hasOwnProperty && |
| !hasProperty && |
| this->directHostObject->HasProperty(propertyId)) |
| { |
| // directHostObject->HasProperty returns true for things in global collections, however, they are not able to be set. |
| // When we call SetProperty we'll return FALSE which means fall back to GlobalObject::SetProperty and shadow our collection |
| // object rather than failing the set altogether. See bug Windows Out Of Band Releases 1144780 and linked bugs for details. |
| if (this->directHostObject->SetProperty(propertyId, value, PropertyOperation_None, info)) |
| { |
| return TRUE; |
| } |
| } |
| else |
| if (this->hostObject && |
| // Consider to revert to the commented line and ignore the prototype chain when direct host is on by default in IE9 mode |
| !hasOwnProperty && |
| !hasProperty && |
| this->hostObject->HasProperty(propertyId)) |
| { |
| return this->hostObject->SetProperty(propertyId, value, PropertyOperation_None, NULL); |
| } |
| |
| if (hasOwnProperty || hasProperty) |
| { |
| return __super::SetRootProperty(propertyId, value, PropertyOperation_None, info); |
| } |
| |
| *setAttempted = FALSE; |
| return FALSE; |
| } |
| |
| BOOL GlobalObject::SetProperty(PropertyId propertyId, Var value, PropertyOperationFlags flags, PropertyValueInfo* info) |
| { |
| Assert(!(flags & PropertyOperation_Root)); |
| |
| BOOL setAttempted = TRUE; |
| if (this->SetExistingProperty(propertyId, value, info, &setAttempted)) |
| { |
| return TRUE; |
| } |
| |
| // |
| // Set was attempted. But the set operation returned false. |
| // This happens, when the property is read only. |
| // In those scenarios, we should be setting the property with default attributes |
| // |
| if (setAttempted) |
| { |
| return FALSE; |
| } |
| |
| // Windows 8 430092: When we set a new property on globalObject there may be stale inline caches holding onto directHostObject->prototype |
| // properties of the same name. So we need to clear proto caches in this scenario. But check for same property in directHostObject->prototype |
| // chain is expensive (call to DOM) compared to just invalidating the cache. |
| // If this blind invalidation is expensive in any scenario then we need to revisit this. |
| // Another solution proposed was not to cache any of the properties of window->directHostObject->prototype. |
| // if ((this->directHostObject && JavascriptOperators::HasProperty(this->directHostObject->GetPrototype(), propertyId)) || |
| // (this->hostObject && JavascriptOperators::HasProperty(this->hostObject->GetPrototype(), propertyId))) |
| |
| this->GetScriptContext()->InvalidateProtoCaches(propertyId); |
| |
| // |
| // x = 10; implicit/phantom variable at global scope |
| // These CAN be deleted |
| // In ES5 they are enumerable. |
| |
| PropertyAttributes attributes = PropertyDynamicTypeDefaults; |
| return DynamicObject::SetPropertyWithAttributes(propertyId, value, attributes, info); |
| } |
| |
| BOOL GlobalObject::SetProperty(JavascriptString* propertyNameString, Var value, PropertyOperationFlags flags, PropertyValueInfo* info) |
| { |
| PropertyRecord const * propertyRecord; |
| this->GetScriptContext()->GetOrAddPropertyRecord(propertyNameString->GetString(), propertyNameString->GetLength(), &propertyRecord); |
| return GlobalObject::SetProperty(propertyRecord->GetPropertyId(), value, flags, info); |
| } |
| |
| BOOL GlobalObject::SetRootProperty(PropertyId propertyId, Var value, PropertyOperationFlags flags, PropertyValueInfo* info) |
| { |
| Assert(flags & PropertyOperation_Root); |
| |
| BOOL setAttempted = TRUE; |
| if (this->SetExistingRootProperty(propertyId, value, info, &setAttempted)) |
| { |
| return TRUE; |
| } |
| |
| // |
| // Set was attempted. But the set operation returned false. |
| // This happens, when the property is read only. |
| // In those scenarios, we should be setting the property with default attributes |
| // |
| if (setAttempted) |
| { |
| return FALSE; |
| } |
| |
| if (flags & PropertyOperation_StrictMode) |
| { |
| if (!GetScriptContext()->GetThreadContext()->RecordImplicitException()) |
| { |
| return FALSE; |
| } |
| JavascriptError::ThrowReferenceError(GetScriptContext(), JSERR_RefErrorUndefVariable); |
| } |
| |
| // Windows 8 430092: When we set a new property on globalObject there may be stale inline caches holding onto directHostObject->prototype |
| // properties of the same name. So we need to clear proto caches in this scenario. But check for same property in directHostObject->prototype |
| // chain is expensive (call to DOM) compared to just invalidating the cache. |
| // If this blind invalidation is expensive in any scenario then we need to revisit this. |
| // Another solution proposed was not to cache any of the properties of window->directHostObject->prototype. |
| // if ((this->directHostObject && JavascriptOperators::HasProperty(this->directHostObject->GetPrototype(), propertyId)) || |
| // (this->hostObject && JavascriptOperators::HasProperty(this->hostObject->GetPrototype(), propertyId))) |
| |
| this->GetScriptContext()->InvalidateProtoCaches(propertyId); |
| |
| return __super::SetRootProperty(propertyId, value, flags, info); |
| } |
| |
| BOOL GlobalObject::SetAccessors(PropertyId propertyId, Var getter, Var setter, PropertyOperationFlags flags) |
| { |
| if (DynamicObject::SetAccessors(propertyId, getter, setter, flags)) |
| { |
| return TRUE; |
| } |
| if (this->directHostObject) |
| { |
| return this->directHostObject->SetAccessors(propertyId, getter, setter, flags); |
| } |
| if (this->hostObject) |
| { |
| return this->hostObject->SetAccessors(propertyId, getter, setter, flags); |
| } |
| return TRUE; |
| } |
| |
| DescriptorFlags GlobalObject::GetSetter(PropertyId propertyId, Var* setterValue, PropertyValueInfo* info, ScriptContext* requestContext) |
| { |
| DescriptorFlags flags = DynamicObject::GetSetter(propertyId, setterValue, info, requestContext); |
| if (flags == None) |
| { |
| if (this->directHostObject) |
| { |
| // We need to look up the prototype chain here. |
| JavascriptOperators::CheckPrototypesForAccessorOrNonWritableProperty(this->directHostObject, propertyId, setterValue, &flags, info, requestContext); |
| } |
| else if (this->hostObject) |
| { |
| JavascriptOperators::CheckPrototypesForAccessorOrNonWritableProperty(this->hostObject, propertyId, setterValue, &flags, info, requestContext); |
| } |
| } |
| |
| return flags; |
| } |
| |
| DescriptorFlags GlobalObject::GetSetter(JavascriptString* propertyNameString, Var* setterValue, PropertyValueInfo* info, ScriptContext* requestContext) |
| { |
| PropertyRecord const* propertyRecord; |
| this->GetScriptContext()->GetOrAddPropertyRecord(propertyNameString->GetString(), propertyNameString->GetLength(), &propertyRecord); |
| return GlobalObject::GetSetter(propertyRecord->GetPropertyId(), setterValue, info, requestContext); |
| } |
| |
| DescriptorFlags GlobalObject::GetRootSetter(PropertyId propertyId, Var* setterValue, PropertyValueInfo* info, ScriptContext* requestContext) |
| { |
| DescriptorFlags flags = __super::GetRootSetter(propertyId, setterValue, info, requestContext); |
| if (flags == None) |
| { |
| if (this->directHostObject) |
| { |
| // We need to look up the prototype chain here. |
| JavascriptOperators::CheckPrototypesForAccessorOrNonWritableProperty(this->directHostObject, propertyId, setterValue, &flags, info, requestContext); |
| } |
| else if (this->hostObject) |
| { |
| JavascriptOperators::CheckPrototypesForAccessorOrNonWritableProperty(this->hostObject, propertyId, setterValue, &flags, info, requestContext); |
| } |
| } |
| |
| return flags; |
| } |
| |
| DescriptorFlags GlobalObject::GetItemSetter(uint32 index, Var* setterValue, ScriptContext* requestContext) |
| { |
| DescriptorFlags flags = DynamicObject::GetItemSetter(index, setterValue, requestContext); |
| if (flags == None) |
| { |
| if (this->directHostObject) |
| { |
| // We need to look up the prototype chain here. |
| JavascriptOperators::CheckPrototypesForAccessorOrNonWritableItem(this->directHostObject, index, setterValue, &flags, requestContext); |
| } |
| else if (this->hostObject) |
| { |
| JavascriptOperators::CheckPrototypesForAccessorOrNonWritableItem(this->hostObject, index, setterValue, &flags, requestContext); |
| } |
| } |
| |
| return flags; |
| } |
| |
| BOOL GlobalObject::DeleteProperty(PropertyId propertyId, PropertyOperationFlags flags) |
| { |
| if (JavascriptConversion::PropertyQueryFlagsToBoolean(__super::HasPropertyQuery(propertyId))) |
| { |
| return __super::DeleteProperty(propertyId, flags); |
| } |
| else if (this->directHostObject && this->directHostObject->HasProperty(propertyId)) |
| { |
| return this->directHostObject->DeleteProperty(propertyId, flags); |
| } |
| else if (this->hostObject && this->hostObject->HasProperty(propertyId)) |
| { |
| return this->hostObject->DeleteProperty(propertyId, flags); |
| } |
| |
| // Non-existent property |
| return TRUE; |
| } |
| |
| BOOL GlobalObject::DeleteProperty(JavascriptString *propertyNameString, PropertyOperationFlags flags) |
| { |
| PropertyRecord const *propertyRecord = nullptr; |
| if (JavascriptOperators::ShouldTryDeleteProperty(this, propertyNameString, &propertyRecord)) |
| { |
| Assert(propertyRecord); |
| return DeleteProperty(propertyRecord->GetPropertyId(), flags); |
| } |
| |
| return TRUE; |
| } |
| |
| BOOL GlobalObject::DeleteRootProperty(PropertyId propertyId, PropertyOperationFlags flags) |
| { |
| if (__super::HasRootProperty(propertyId)) |
| { |
| return __super::DeleteRootProperty(propertyId, flags); |
| } |
| else if (this->directHostObject && this->directHostObject->HasProperty(propertyId)) |
| { |
| return this->directHostObject->DeleteProperty(propertyId, flags); |
| } |
| else if (this->hostObject && this->hostObject->HasProperty(propertyId)) |
| { |
| return this->hostObject->DeleteProperty(propertyId, flags); |
| } |
| |
| // Non-existent property |
| return TRUE; |
| } |
| |
| PropertyQueryFlags GlobalObject::HasItemQuery(uint32 index) |
| { |
| return JavascriptConversion::BooleanToPropertyQueryFlags(JavascriptConversion::PropertyQueryFlagsToBoolean((DynamicObject::HasItemQuery(index))) |
| || (this->directHostObject && JavascriptOperators::HasItem(this->directHostObject, index)) |
| || (this->hostObject && JavascriptOperators::HasItem(this->hostObject, index))); |
| } |
| |
| BOOL GlobalObject::HasOwnItem(uint32 index) |
| { |
| return JavascriptConversion::PropertyQueryFlagsToBoolean(DynamicObject::HasItemQuery(index)) |
| || (this->directHostObject && this->directHostObject->HasItem(index)); |
| } |
| |
| PropertyQueryFlags GlobalObject::GetItemReferenceQuery(Var originalInstance, uint32 index, Var* value, ScriptContext* requestContext) |
| { |
| if (JavascriptConversion::PropertyQueryFlagsToBoolean(DynamicObject::GetItemReferenceQuery(originalInstance, index, value, requestContext))) |
| { |
| return Property_Found; |
| } |
| return JavascriptConversion::BooleanToPropertyQueryFlags( |
| (this->directHostObject && JavascriptConversion::PropertyQueryFlagsToBoolean(this->directHostObject->GetItemReferenceQuery(originalInstance, index, value, requestContext))) || |
| (this->hostObject && JavascriptConversion::PropertyQueryFlagsToBoolean(this->hostObject->GetItemReferenceQuery(originalInstance, index, value, requestContext)))); |
| } |
| |
| BOOL GlobalObject::SetItem(uint32 index, Var value, PropertyOperationFlags flags) |
| { |
| BOOL result = DynamicObject::SetItem(index, value, flags); |
| return result; |
| } |
| |
| PropertyQueryFlags GlobalObject::GetItemQuery(Var originalInstance, uint32 index, Var* value, ScriptContext* requestContext) |
| { |
| if (JavascriptConversion::PropertyQueryFlagsToBoolean(DynamicObject::GetItemQuery(originalInstance, index, value, requestContext))) |
| { |
| return Property_Found; |
| } |
| |
| return JavascriptConversion::BooleanToPropertyQueryFlags((this->directHostObject && this->directHostObject->GetItem(originalInstance, index, value, requestContext)) || |
| (this->hostObject && this->hostObject->GetItem(originalInstance, index, value, requestContext))); |
| } |
| |
| BOOL GlobalObject::DeleteItem(uint32 index, PropertyOperationFlags flags) |
| { |
| if (DynamicObject::DeleteItem(index, flags)) |
| { |
| return TRUE; |
| } |
| |
| if (this->directHostObject) |
| { |
| return this->directHostObject->DeleteItem(index, flags); |
| } |
| |
| if (this->hostObject) |
| { |
| return this->hostObject->DeleteItem(index, flags); |
| } |
| return FALSE; |
| } |
| |
| BOOL GlobalObject::GetDiagValueString(StringBuilder<ArenaAllocator>* stringBuilder, ScriptContext* requestContext) |
| { |
| stringBuilder->AppendCppLiteral(_u("{...}")); |
| return TRUE; |
| } |
| |
| BOOL GlobalObject::GetDiagTypeString(StringBuilder<ArenaAllocator>* stringBuilder, ScriptContext* requestContext) |
| { |
| stringBuilder->AppendCppLiteral(_u("Object, (Global)")); |
| return TRUE; |
| } |
| |
| BOOL GlobalObject::StrictEquals(__in Js::Var other, __out BOOL* value, ScriptContext * requestContext) |
| { |
| if (this == other) |
| { |
| *value = TRUE; |
| return TRUE; |
| } |
| else if (this->directHostObject) |
| { |
| return this->directHostObject->StrictEquals(other, value, requestContext); |
| } |
| else if (this->hostObject) |
| { |
| return this->hostObject->StrictEquals(other, value, requestContext); |
| } |
| *value = FALSE; |
| return FALSE; |
| } |
| |
| BOOL GlobalObject::Equals(__in Js::Var other, __out BOOL* value, ScriptContext * requestContext) |
| { |
| if (this == other) |
| { |
| *value = TRUE; |
| return TRUE; |
| } |
| else if (this->directHostObject) |
| { |
| return this->directHostObject->Equals(other, value, requestContext); |
| } |
| else if (this->hostObject) |
| { |
| return this->hostObject->Equals(other, value, requestContext); |
| } |
| |
| *value = FALSE; |
| return TRUE; |
| } |
| |
| #if ENABLE_TTD |
| TTD::NSSnapObjects::SnapObjectType GlobalObject::GetSnapTag_TTD() const |
| { |
| return TTD::NSSnapObjects::SnapObjectType::SnapWellKnownObject; |
| } |
| |
| void GlobalObject::ExtractSnapObjectDataInto(TTD::NSSnapObjects::SnapObject* objData, TTD::SlabAllocator& alloc) |
| { |
| // |
| //TODO: we assume that the global object is always set right (e.g., ignore the directHostObject and secureDirectHostObject values) we need to verify that this is ok and/or what to do about it |
| // |
| |
| TTD::NSSnapObjects::StdExtractSetKindSpecificInfo<void*, TTD::NSSnapObjects::SnapObjectType::SnapWellKnownObject>(objData, nullptr); |
| } |
| #endif |
| } |