// In some cases we delay throw from helper methods and return ThrowErrorObject instead which we call and throw later. // Then the exception is actually thrown when we call this method. Var ThrowErrorObject::DefaultEntryPoint(RecyclableObject* function, CallInfo callInfo, ...) { ARGUMENTS(args, callInfo); ScriptContext* scriptContext = function->GetScriptContext(); ThrowErrorObject* throwErrorObject = ThrowErrorObject::FromVar(function); #ifdef ENABLE_SCRIPT_DEBUGGING bool useExceptionWrapper = scriptContext->IsScriptContextInDebugMode() /* Check for script context is intentional as library code also uses exception wrapper */ && (ScriptContext::IsExceptionWrapperForBuiltInsEnabled(scriptContext) || ScriptContext::IsExceptionWrapperForHelpersEnabled(scriptContext)) && !AutoRegisterIgnoreExceptionWrapper::IsRegistered(scriptContext->GetThreadContext()); if (useExceptionWrapper) { // Forward the throw via regular try-catch wrapper logic that we use for helper/library calls. AutoRegisterIgnoreExceptionWrapper autoWrapper(scriptContext->GetThreadContext()); Var ret = HelperOrLibraryMethodWrapper<true>(scriptContext, [throwErrorObject, scriptContext]() -> Var { JavascriptExceptionOperators::Throw(throwErrorObject->m_error, scriptContext); }); return ret; } else #endif { JavascriptExceptionOperators::Throw(throwErrorObject->m_error, scriptContext); } }
Var JavascriptExternalFunction::StdCallExternalFunctionThunk(RecyclableObject* function, CallInfo callInfo, ...) { RUNTIME_ARGUMENTS(args, callInfo); JavascriptExternalFunction* externalFunction = static_cast<JavascriptExternalFunction*>(function); externalFunction->PrepareExternalCall(&args); ScriptContext * scriptContext = externalFunction->type->GetScriptContext(); AnalysisAssert(scriptContext); Var result = NULL; BEGIN_LEAVE_SCRIPT(scriptContext) { result = externalFunction->stdCallNativeMethod(function, ((callInfo.Flags & CallFlags_New) != 0), args.Values, args.Info.Count, externalFunction->callbackState); } END_LEAVE_SCRIPT(scriptContext); if (result != nullptr && !Js::TaggedNumber::Is(result)) { if (!Js::RecyclableObject::Is(result)) { Js::Throw::InternalError(); } Js::RecyclableObject * obj = Js::RecyclableObject::FromVar(result); // For JSRT, we could get result marshalled in different context. bool isJSRT = !(scriptContext->GetThreadContext()->GetIsThreadBound()); if (!isJSRT && obj->GetScriptContext() != scriptContext) { Js::Throw::InternalError(); } } if (scriptContext->HasRecordedException()) { bool considerPassingToDebugger = false; JavascriptExceptionObject* recordedException = scriptContext->GetAndClearRecordedException(&considerPassingToDebugger); if (recordedException != nullptr) { // If this is script termination, then throw ScriptAbortExceptio, else throw normal Exception object. if (recordedException == scriptContext->GetThreadContext()->GetPendingTerminatedErrorObject()) { throw Js::ScriptAbortException(); } else { JavascriptExceptionOperators::RethrowExceptionObject(recordedException, scriptContext, considerPassingToDebugger); } } } return externalFunction->FinishExternalCall(result); }
BreakpointProbe* DebugDocument::SetBreakPoint(StatementLocation statement, BREAKPOINT_STATE bps) { ScriptContext* scriptContext = this->utf8SourceInfo->GetScriptContext(); if (scriptContext == nullptr || scriptContext->IsClosed()) { return nullptr; } switch (bps) { default: AssertMsg(FALSE, "Bad breakpoint state"); // Fall thru case BREAKPOINT_DISABLED: case BREAKPOINT_DELETED: { BreakpointProbeList* pBreakpointList = this->GetBreakpointList(); if (pBreakpointList) { ArenaAllocator arena(_u("TemporaryBreakpointList"), scriptContext->GetThreadContext()->GetDebugManager()->GetDiagnosticPageAllocator(), Throw::OutOfMemory); BreakpointProbeList* pDeleteList = this->NewBreakpointList(&arena); pBreakpointList->Map([&statement, scriptContext, pDeleteList](int index, BreakpointProbe * breakpointProbe) { if (breakpointProbe->Matches(statement.function, statement.statement.begin)) { scriptContext->GetDebugContext()->GetProbeContainer()->RemoveProbe(breakpointProbe); pDeleteList->Add(breakpointProbe); } }); pDeleteList->Map([pBreakpointList](int index, BreakpointProbe * breakpointProbe) { pBreakpointList->Remove(breakpointProbe); }); pDeleteList->Clear(); } break; } case BREAKPOINT_ENABLED: { BreakpointProbe* pProbe = Anew(scriptContext->AllocatorForDiagnostics(), BreakpointProbe, this, statement, scriptContext->GetThreadContext()->GetDebugManager()->GetNextBreakpointId()); scriptContext->GetDebugContext()->GetProbeContainer()->AddProbe(pProbe); BreakpointProbeList* pBreakpointList = this->GetBreakpointList(); pBreakpointList->Add(pProbe); return pProbe; break; } } return nullptr; }
// Symbol.keyFor as described in ES 2015 Var JavascriptSymbol::EntryKeyFor(RecyclableObject* function, CallInfo callInfo, ...) { PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault); ARGUMENTS(args, callInfo); AssertMsg(args.Info.Count, "Should always have implicit 'this'."); ScriptContext* scriptContext = function->GetScriptContext(); JavascriptLibrary* library = scriptContext->GetLibrary(); Assert(!(callInfo.Flags & CallFlags_New)); if (args.Info.Count < 2 || !JavascriptSymbol::Is(args[1])) { JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedSymbol, _u("Symbol.keyFor")); } JavascriptSymbol* sym = JavascriptSymbol::FromVar(args[1]); const char16* key = sym->GetValue()->GetBuffer(); // Search the global symbol registration map for a key equal to the description of the symbol passed into Symbol.keyFor. // Symbol.for creates a new symbol with description equal to the key and uses that key as a mapping to the new symbol. // There will only be one symbol in the map with that string key value. const Js::PropertyRecord* propertyRecord = scriptContext->GetThreadContext()->GetSymbolFromRegistrationMap(key); // If we found a PropertyRecord in the map, make sure it is the same symbol that was passed to Symbol.keyFor. // If the two are different, it means the symbol passed to keyFor has the same description as a symbol registered via // Symbol.for _but_ is not the symbol returned from Symbol.for. if (propertyRecord != nullptr && propertyRecord == sym->GetValue()) { return JavascriptString::NewCopyBuffer(key, sym->GetValue()->GetLength(), scriptContext); } return library->GetUndefined(); }
void ArrayObject::ThrowItemNotConfigurableError(PropertyId propId /*= Constants::NoProperty*/) { ScriptContext* scriptContext = GetScriptContext(); JavascriptError::ThrowTypeError(scriptContext, JSERR_DefineProperty_NotConfigurable, propId != Constants::NoProperty ? scriptContext->GetThreadContext()->GetPropertyName(propId)->GetBuffer() : nullptr); }
Var JavascriptExternalFunction::WrappedFunctionThunk(RecyclableObject* function, CallInfo callInfo, ...) { RUNTIME_ARGUMENTS(args, callInfo); JavascriptExternalFunction* externalFunction = static_cast<JavascriptExternalFunction*>(function); ScriptContext* scriptContext = externalFunction->type->GetScriptContext(); Assert(!scriptContext->GetThreadContext()->IsDisableImplicitException()); scriptContext->VerifyAlive(); Assert(scriptContext->GetThreadContext()->IsScriptActive()); // Make sure the callee knows we are a wrapped function thunk args.Info.Flags = (Js::CallFlags) (((long) args.Info.Flags) | CallFlags_Wrapped); // don't need to leave script here, ExternalFunctionThunk will Assert(externalFunction->wrappedMethod->GetFunctionInfo()->GetOriginalEntryPoint() == JavascriptExternalFunction::ExternalFunctionThunk); return JavascriptFunction::CallFunction<true>(externalFunction->wrappedMethod, externalFunction->wrappedMethod->GetEntryPoint(), args); }
Var JavascriptGeneratorFunction::EntryGeneratorFunctionImplementation(RecyclableObject* function, CallInfo callInfo, ...) { PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault); ARGUMENTS(stackArgs, callInfo); Assert(!(callInfo.Flags & CallFlags_New)); ScriptContext* scriptContext = function->GetScriptContext(); JavascriptGeneratorFunction* generatorFunction = JavascriptGeneratorFunction::FromVar(function); // InterpreterStackFrame takes a pointer to the args, so copy them to the recycler heap // and use that buffer for this InterpreterStackFrame. Field(Var)* argsHeapCopy = RecyclerNewArray(scriptContext->GetRecycler(), Field(Var), stackArgs.Info.Count); CopyArray(argsHeapCopy, stackArgs.Info.Count, stackArgs.Values, stackArgs.Info.Count); Arguments heapArgs(callInfo, (Var*)argsHeapCopy); DynamicObject* prototype = scriptContext->GetLibrary()->CreateGeneratorConstructorPrototypeObject(); JavascriptGenerator* generator = scriptContext->GetLibrary()->CreateGenerator(heapArgs, generatorFunction->scriptFunction, prototype); // Set the prototype from constructor JavascriptOperators::OrdinaryCreateFromConstructor(function, generator, prototype, scriptContext); // Call a next on the generator to execute till the beginning of the body CALL_ENTRYPOINT(scriptContext->GetThreadContext(), generator->EntryNext, function, CallInfo(CallFlags_Value, 1), generator); return generator; }
void __cdecl JavascriptExternalFunction::DeferredInitializer(DynamicObject* instance, DeferredTypeHandlerBase* typeHandler, DeferredInitializeMode mode) { JavascriptExternalFunction* object = static_cast<JavascriptExternalFunction*>(instance); HRESULT hr = E_FAIL; ScriptContext* scriptContext = object->GetScriptContext(); AnalysisAssert(scriptContext); // Don't call the implicit call if disable implicit call if (scriptContext->GetThreadContext()->IsDisableImplicitCall()) { scriptContext->GetThreadContext()->AddImplicitCallFlags(ImplicitCall_External); //we will return if we get call further into implicitcalls. return; } if (scriptContext->IsClosed() || scriptContext->IsInvalidatedForHostObjects()) { Js::JavascriptError::MapAndThrowError(scriptContext, E_ACCESSDENIED); } ThreadContext* threadContext = scriptContext->GetThreadContext(); typeHandler->Convert(instance, mode, object->typeSlots, object->hasAccessors); BEGIN_LEAVE_SCRIPT_INTERNAL(scriptContext) { ASYNC_HOST_OPERATION_START(threadContext); hr = object->initMethod(instance); ASYNC_HOST_OPERATION_END(threadContext); } END_LEAVE_SCRIPT_INTERNAL(scriptContext); if (FAILED(hr)) { Js::JavascriptError::MapAndThrowError(scriptContext, hr); } JavascriptString * functionName = nullptr; if (scriptContext->GetConfig()->IsES6FunctionNameEnabled() && object->GetFunctionName(&functionName)) { object->SetPropertyWithAttributes(PropertyIds::name, functionName, PropertyConfigurable, nullptr); } }
HRESULT JavascriptError::GetRuntimeErrorWithScriptEnter(RecyclableObject* errorObject, __out_opt LPCWSTR * pMessage) { ScriptContext* scriptContext = errorObject->GetScriptContext(); Assert(!scriptContext->GetThreadContext()->IsScriptActive()); // Use _NOT_SCRIPT. We enter runtime to get error info, likely inside a catch. BEGIN_JS_RUNTIME_CALL_NOT_SCRIPT(scriptContext) { return GetRuntimeError(errorObject, pMessage); } END_JS_RUNTIME_CALL(scriptContext); }
Var CrossSite::ProfileThunk(RecyclableObject* callable, CallInfo callInfo, ...) { JavascriptFunction* function = JavascriptFunction::FromVar(callable); Assert(function->GetTypeId() == TypeIds_Function); Assert(function->GetEntryPoint() == CrossSite::ProfileThunk); RUNTIME_ARGUMENTS(args, callInfo); ScriptContext * scriptContext = function->GetScriptContext(); // It is not safe to access the function body if the script context is not alive. scriptContext->VerifyAliveWithHostContext(!function->IsExternal(), scriptContext->GetThreadContext()->GetPreviousHostScriptContext()); JavascriptMethod entryPoint; FunctionInfo *funcInfo = function->GetFunctionInfo(); TTD_XSITE_LOG(callable->GetScriptContext(), "DefaultOrProfileThunk", callable); #ifdef ENABLE_WASM if (WasmScriptFunction::Is(function)) { AsmJsFunctionInfo* asmInfo = funcInfo->GetFunctionBody()->GetAsmJsFunctionInfo(); Assert(asmInfo); if (asmInfo->IsWasmDeferredParse()) { entryPoint = WasmLibrary::WasmDeferredParseExternalThunk; } else { entryPoint = Js::AsmJsExternalEntryPoint; } } else #endif if (funcInfo->HasBody()) { #if ENABLE_DEBUG_CONFIG_OPTIONS char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE]; #endif entryPoint = ScriptFunction::FromVar(function)->GetEntryPointInfo()->jsMethod; if (funcInfo->IsDeferred() && scriptContext->IsProfiling()) { // if the current entrypoint is deferred parse we need to update it appropriately for the profiler mode. entryPoint = Js::ScriptContext::GetProfileModeThunk(entryPoint); } OUTPUT_TRACE(Js::ScriptProfilerPhase, _u("CrossSite::ProfileThunk FunctionNumber : %s, Entrypoint : 0x%08X\n"), funcInfo->GetFunctionProxy()->GetDebugNumberSet(debugStringBuffer), entryPoint); } else { entryPoint = ProfileEntryThunk; } return CommonThunk(function, entryPoint, args); }
HRESULT JavascriptError::GetRuntimeError(RecyclableObject* errorObject, __out_opt LPCWSTR * pMessage) { // Only report the error number if it is a runtime error HRESULT hr = JSERR_UncaughtException; ScriptContext* scriptContext = errorObject->GetScriptContext(); // This version needs to be called in script. Assert(scriptContext->GetThreadContext()->IsScriptActive()); Var number = JavascriptOperators::GetProperty(errorObject, Js::PropertyIds::number, scriptContext, NULL); if (TaggedInt::Is(number)) { hr = TaggedInt::ToInt32(number); } else if (JavascriptNumber::Is_NoTaggedIntCheck(number)) { hr = (HRESULT)JavascriptNumber::GetValue(number); } if (!FAILED(hr)) { hr = E_FAIL; } if (pMessage != NULL) { *pMessage = _u(""); // default to have IE load the error message, by returning empty-string // The description property always overrides any error message Var description = Js::JavascriptOperators::GetProperty(errorObject, Js::PropertyIds::description, scriptContext, NULL); if (JavascriptString::Is(description)) { // Always report the description to IE if it is a string, even if the user sets it JavascriptString * messageString = JavascriptString::FromVar(description); *pMessage = messageString->GetSz(); } else if (Js::JavascriptError::Is(errorObject) && Js::JavascriptError::FromVar(errorObject)->originalRuntimeErrorMessage != nullptr) { // use the runtime error message *pMessage = Js::JavascriptError::FromVar(errorObject)->originalRuntimeErrorMessage; } else if (FACILITY_CONTROL == HRESULT_FACILITY(hr)) { // User might have create it's own Error object with JS error code, try to load the // resource string from the HResult by returning null; *pMessage = nullptr; } } // If neither the description or original runtime error message is set, and there are no error message. // Then just return false and we will report Uncaught exception to IE. return hr; }
// Symbol.for as described in ES 2015 Var JavascriptSymbol::EntryFor(RecyclableObject* function, CallInfo callInfo, ...) { PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault); ARGUMENTS(args, callInfo); AssertMsg(args.Info.Count, "Should always have implicit 'this'."); ScriptContext* scriptContext = function->GetScriptContext(); JavascriptLibrary* library = scriptContext->GetLibrary(); Assert(!(callInfo.Flags & CallFlags_New)); JavascriptString* key; if (args.Info.Count > 1) { key = JavascriptConversion::ToString(args[1], scriptContext); } else { key = library->GetUndefinedDisplayString(); } // Search the global symbol registration map for a symbol with description equal to the string key. // The map can only have one symbol with that description so if we found a symbol, that is the registered // symbol for the string key. const Js::PropertyRecord* propertyRecord = scriptContext->GetThreadContext()->GetSymbolFromRegistrationMap(key->GetString(), key->GetLength()); // If we didn't find a PropertyRecord in the map, we'll create a new symbol with description equal to the key string. // This is the only place we add new PropertyRecords to the map, so we should never have multiple PropertyRecords in the // map with the same string key value (since we would return the one we found above instead of creating a new one). if (propertyRecord == nullptr) { propertyRecord = scriptContext->GetThreadContext()->AddSymbolToRegistrationMap(key->GetString(), key->GetLength()); } Assert(propertyRecord != nullptr); return library->CreateSymbol(propertyRecord); }
Var __stdcall JavascriptExternalFunction::TTDReplayDummyExternalMethod(Var callee, Var *args, USHORT cargs, StdCallJavascriptMethodInfo *info, void *callbackState) { JavascriptExternalFunction* externalFunction = static_cast<JavascriptExternalFunction*>(callee); ScriptContext* scriptContext = externalFunction->type->GetScriptContext(); TTD::EventLog* elog = scriptContext->GetThreadContext()->TTDLog; TTDAssert(elog != nullptr, "How did this get created then???"); //If this flag is set then this is ok (the debugger may be evaluating this so just return undef -- otherwise this is an error if(!elog->IsDebugModeFlagSet()) { TTDAssert(false, "This should never be reached in pure replay mode!!!"); return nullptr; } return scriptContext->GetLibrary()->GetUndefined(); }
Var JavascriptGeneratorFunction::EntryAsyncFunctionImplementation(RecyclableObject* function, CallInfo callInfo, ...) { PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault); ARGUMENTS(stackArgs, callInfo); ScriptContext* scriptContext = function->GetScriptContext(); JavascriptLibrary* library = scriptContext->GetLibrary(); RecyclableObject* prototype = scriptContext->GetLibrary()->GetNull(); // InterpreterStackFrame takes a pointer to the args, so copy them to the recycler heap // and use that buffer for this InterpreterStackFrame. Field(Var)* argsHeapCopy = RecyclerNewArray(scriptContext->GetRecycler(), Field(Var), stackArgs.Info.Count); CopyArray(argsHeapCopy, stackArgs.Info.Count, stackArgs.Values, stackArgs.Info.Count); Arguments heapArgs(callInfo, (Var*)argsHeapCopy); JavascriptExceptionObject* e = nullptr; JavascriptPromiseResolveOrRejectFunction* resolve; JavascriptPromiseResolveOrRejectFunction* reject; JavascriptPromiseAsyncSpawnExecutorFunction* executor = library->CreatePromiseAsyncSpawnExecutorFunction( JavascriptPromise::EntryJavascriptPromiseAsyncSpawnExecutorFunction, scriptContext->GetLibrary()->CreateGenerator(heapArgs, JavascriptAsyncFunction::FromVar(function)->GetGeneratorVirtualScriptFunction(), prototype), stackArgs[0]); JavascriptPromise* promise = library->CreatePromise(); JavascriptPromise::InitializePromise(promise, &resolve, &reject, scriptContext); try { CALL_FUNCTION(scriptContext->GetThreadContext(), executor, CallInfo(CallFlags_Value, 3), library->GetUndefined(), resolve, reject); } catch (const JavascriptException& err) { e = err.GetAndClear(); } if (e != nullptr) { JavascriptPromise::TryRejectWithExceptionObject(e, reject, scriptContext); } return promise; }
void DynamicObjectPropertyEnumerator::Reset() { if (this->object) { enumeratedCount = 0; initialType = object->GetDynamicType(); objectIndex = Constants::NoBigSlot; initialPropertyCount = GetSnapShotSemantics() ? this->object->GetPropertyCount() : Constants::NoBigSlot; // Create the appropriate enumerator object. if (GetSnapShotSemantics() && this->initialType->PrepareForTypeSnapshotEnumeration()) { ScriptContext* scriptContext = this->object->GetScriptContext(); ThreadContext * threadContext = scriptContext->GetThreadContext(); CachedData * data = (CachedData *)threadContext->GetDynamicObjectEnumeratorCache(this->initialType); if (data == nullptr || data->scriptContext != this->requestContext || data->enumNonEnumerable != GetEnumNonEnumerable() || data->enumSymbols != GetEnumSymbols()) { data = RecyclerNewStructPlus(scriptContext->GetRecycler(), this->initialPropertyCount * sizeof(PropertyString *) + this->initialPropertyCount * sizeof(BigPropertyIndex) + this->initialPropertyCount * sizeof(PropertyAttributes), CachedData); data->scriptContext = requestContext; data->cachedCount = 0; data->strings = (PropertyString **)(data + 1); data->indexes = (BigPropertyIndex *)(data->strings + this->initialPropertyCount); data->attributes = (PropertyAttributes*)(data->indexes + this->initialPropertyCount); data->completed = false; data->enumNonEnumerable = GetEnumNonEnumerable(); data->enumSymbols = GetEnumSymbols(); threadContext->AddDynamicObjectEnumeratorCache(this->initialType, data); } this->cachedData = data; this->cachedDataType = this->initialType; } else { this->cachedData = nullptr; this->cachedDataType = nullptr; } } }
Var JavascriptExternalFunction::StdCallExternalFunctionThunk(RecyclableObject* function, CallInfo callInfo, ...) { ARGUMENTS(args, callInfo); JavascriptExternalFunction* externalFunction = static_cast<JavascriptExternalFunction*>(function); externalFunction->PrepareExternalCall(&args); ScriptContext * scriptContext = externalFunction->type->GetScriptContext(); AnalysisAssert(scriptContext); if (args.Info.Count > USHORT_MAX) { // Due to compat reasons, stdcall external functions expect a ushort count of args. // To support more than this we will need a new API. Js::JavascriptError::ThrowTypeError(scriptContext, JSERR_ArgListTooLarge); } Var result = nullptr; Assert(callInfo.Count > 0); StdCallJavascriptMethodInfo info = { args[0], args.HasNewTarget() ? args.GetNewTarget() : args.IsNewCall() ? function : scriptContext->GetLibrary()->GetUndefined(), args.IsNewCall() }; #if ENABLE_TTD if(scriptContext->ShouldPerformRecordOrReplayAction()) { result = JavascriptExternalFunction::HandleRecordReplayExternalFunction_StdThunk(function, callInfo, args, scriptContext); } else { BEGIN_LEAVE_SCRIPT(scriptContext) { result = externalFunction->stdCallNativeMethod(function, args.Values, static_cast<USHORT>(args.Info.Count), &info, externalFunction->callbackState); } END_LEAVE_SCRIPT(scriptContext); } #else BEGIN_LEAVE_SCRIPT(scriptContext) { result = externalFunction->stdCallNativeMethod(function, args.Values, static_cast<USHORT>(args.Info.Count), &info, externalFunction->callbackState); } END_LEAVE_SCRIPT(scriptContext); #endif bool marshallingMayBeNeeded = false; if (result != nullptr) { marshallingMayBeNeeded = Js::RecyclableObject::Is(result); if (marshallingMayBeNeeded) { Js::RecyclableObject * obj = Js::RecyclableObject::FromVar(result); // For JSRT, we could get result marshalled in different context. bool isJSRT = scriptContext->GetThreadContext()->IsJSRT(); marshallingMayBeNeeded = obj->GetScriptContext() != scriptContext; if (!isJSRT && marshallingMayBeNeeded) { Js::Throw::InternalError(); } } } if (scriptContext->HasRecordedException()) { bool considerPassingToDebugger = false; JavascriptExceptionObject* recordedException = scriptContext->GetAndClearRecordedException(&considerPassingToDebugger); if (recordedException != nullptr) { // If this is script termination, then throw ScriptAbortExceptio, else throw normal Exception object. if (recordedException == scriptContext->GetThreadContext()->GetPendingTerminatedErrorObject()) { throw Js::ScriptAbortException(); } else { JavascriptExceptionOperators::RethrowExceptionObject(recordedException, scriptContext, considerPassingToDebugger); } } } if (result == nullptr) { result = scriptContext->GetLibrary()->GetUndefined(); } else if (marshallingMayBeNeeded) { result = CrossSite::MarshalVar(scriptContext, result); } return result; }
void JavascriptExternalFunction::PrepareExternalCall(Js::Arguments * args) { ScriptContext * scriptContext = this->type->GetScriptContext(); Assert(!scriptContext->GetThreadContext()->IsDisableImplicitException()); scriptContext->VerifyAlive(); Assert(scriptContext->GetThreadContext()->IsScriptActive()); if (args->Info.Count == 0) { JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined); } Var &thisVar = args->Values[0]; Js::TypeId typeId = Js::JavascriptOperators::GetTypeId(thisVar); Js::RecyclableObject* directHostObject = nullptr; switch(typeId) { case TypeIds_Integer: #if FLOATVAR case TypeIds_Number: #endif // FLOATVAR Assert(!Js::RecyclableObject::Is(thisVar)); break; default: { Assert(Js::RecyclableObject::Is(thisVar)); ScriptContext* scriptContextThisVar = Js::RecyclableObject::FromVar(thisVar)->GetScriptContext(); // We need to verify "this" pointer is active as well. The problem is that DOM prototype functions are // the same across multiple frames, and caller can do function.call(closedthis) Assert(!scriptContext->GetThreadContext()->IsDisableImplicitException()); scriptContextThisVar->VerifyAlive(); // translate direct host for fastDOM. switch(typeId) { case Js::TypeIds_GlobalObject: { Js::GlobalObject* srcGlobalObject = (Js::GlobalObject*)(void*)(thisVar); directHostObject = srcGlobalObject->GetDirectHostObject(); // For jsrt, direct host object can be null. If thats the case don't change it. if (directHostObject != nullptr) { thisVar = directHostObject; } } break; case Js::TypeIds_Undefined: case Js::TypeIds_Null: { // Call to DOM function with this as "undefined" or "null" // This should be converted to Global object Js::GlobalObject* srcGlobalObject = scriptContextThisVar->GetGlobalObject() ; directHostObject = srcGlobalObject->GetDirectHostObject(); // For jsrt, direct host object can be null. If thats the case don't change it. if (directHostObject != nullptr) { thisVar = directHostObject; } } break; } } break; } }
Var JavascriptExternalFunction::StdCallExternalFunctionThunk(RecyclableObject* function, CallInfo callInfo, ...) { ARGUMENTS(args, callInfo); JavascriptExternalFunction* externalFunction = static_cast<JavascriptExternalFunction*>(function); externalFunction->PrepareExternalCall(&args); ScriptContext * scriptContext = externalFunction->type->GetScriptContext(); AnalysisAssert(scriptContext); Var result = NULL; #if ENABLE_TTD if(scriptContext->ShouldPerformRecordOrReplayAction()) { result = JavascriptExternalFunction::HandleRecordReplayExternalFunction_StdThunk(function, callInfo, args, scriptContext); } else { BEGIN_LEAVE_SCRIPT(scriptContext) { result = externalFunction->stdCallNativeMethod(function, ((callInfo.Flags & CallFlags_New) != 0), args.Values, args.Info.Count, externalFunction->callbackState); } END_LEAVE_SCRIPT(scriptContext); } #else BEGIN_LEAVE_SCRIPT(scriptContext) { result = externalFunction->stdCallNativeMethod(function, ((callInfo.Flags & CallFlags_New) != 0), args.Values, args.Info.Count, externalFunction->callbackState); } END_LEAVE_SCRIPT(scriptContext); #endif if (result != nullptr && !Js::TaggedNumber::Is(result)) { if (!Js::RecyclableObject::Is(result)) { Js::Throw::InternalError(); } Js::RecyclableObject * obj = Js::RecyclableObject::FromVar(result); // For JSRT, we could get result marshalled in different context. bool isJSRT = scriptContext->GetThreadContext()->IsJSRT(); if (!isJSRT && obj->GetScriptContext() != scriptContext) { Js::Throw::InternalError(); } } if (scriptContext->HasRecordedException()) { bool considerPassingToDebugger = false; JavascriptExceptionObject* recordedException = scriptContext->GetAndClearRecordedException(&considerPassingToDebugger); if (recordedException != nullptr) { // If this is script termination, then throw ScriptAbortExceptio, else throw normal Exception object. if (recordedException == scriptContext->GetThreadContext()->GetPendingTerminatedErrorObject()) { throw Js::ScriptAbortException(); } else { JavascriptExceptionOperators::RethrowExceptionObject(recordedException, scriptContext, considerPassingToDebugger); } } } if (result == nullptr) { result = scriptContext->GetLibrary()->GetUndefined(); } else { result = CrossSite::MarshalVar(scriptContext, result); } return result; }
Var CrossSite::CommonThunk(RecyclableObject* recyclableObject, JavascriptMethod entryPoint, Arguments args) { DynamicObject* function = DynamicObject::FromVar(recyclableObject); FunctionInfo * functionInfo = (JavascriptFunction::Is(function) ? JavascriptFunction::FromVar(function)->GetFunctionInfo() : nullptr); AutoDisableRedeferral autoDisableRedeferral(functionInfo); ScriptContext* targetScriptContext = function->GetScriptContext(); Assert(!targetScriptContext->IsClosed()); Assert(function->IsExternal() || function->IsCrossSiteObject()); Assert(targetScriptContext->GetThreadContext()->IsScriptActive()); HostScriptContext* calleeHostScriptContext = targetScriptContext->GetHostScriptContext(); HostScriptContext* callerHostScriptContext = targetScriptContext->GetThreadContext()->GetPreviousHostScriptContext(); if (callerHostScriptContext == calleeHostScriptContext || (callerHostScriptContext == nullptr && !calleeHostScriptContext->HasCaller())) { return JavascriptFunction::CallFunction<true>(function, entryPoint, args, true /*useLargeArgCount*/); } #if DBG_DUMP || defined(PROFILE_EXEC) || defined(PROFILE_MEM) calleeHostScriptContext->EnsureParentInfo(callerHostScriptContext->GetScriptContext()); #endif TTD_XSITE_LOG(recyclableObject->GetScriptContext(), "CommonThunk -- Pass Through", recyclableObject); uint i = 0; if (args.Values[0] == nullptr) { i = 1; Assert(args.IsNewCall()); Assert(JavascriptProxy::Is(function) || (JavascriptFunction::Is(function) && JavascriptFunction::FromVar(function)->GetFunctionInfo()->GetAttributes() & FunctionInfo::SkipDefaultNewObject)); } uint count = args.Info.Count; for (; i < count; i++) { args.Values[i] = CrossSite::MarshalVar(targetScriptContext, args.Values[i]); } if (args.HasExtraArg()) { // The final eval arg is a frame display that needs to be marshaled specially. args.Values[count] = CrossSite::MarshalFrameDisplay(targetScriptContext, args.GetFrameDisplay()); } #if ENABLE_NATIVE_CODEGEN CheckCodeGenFunction checkCodeGenFunction = GetCheckCodeGenFunction(entryPoint); if (checkCodeGenFunction != nullptr) { ScriptFunction* callFunc = ScriptFunction::FromVar(function); entryPoint = checkCodeGenFunction(callFunc); Assert(CrossSite::IsThunk(function->GetEntryPoint())); } #endif // We need to setup the caller chain when we go across script site boundary. Property access // is OK, and we need to let host know who the caller is when a call is from another script site. // CrossSiteObject is the natural place but it is in the target site. We build up the site // chain through PushDispatchExCaller/PopDispatchExCaller, and we call SetCaller in the target site // to indicate who the caller is. We first need to get the site from the previously pushed site // and set that as the caller for current call, and push a new DispatchExCaller for future calls // off this site. GetDispatchExCaller and ReleaseDispatchExCaller is used to get the current caller. // currentDispatchExCaller is cached to avoid multiple allocations. IUnknown* sourceCaller = nullptr, *previousSourceCaller = nullptr; HRESULT hr = NOERROR; Var result = nullptr; BOOL wasDispatchExCallerPushed = FALSE, wasCallerSet = FALSE; TryFinally([&]() { hr = callerHostScriptContext->GetDispatchExCaller((void**)&sourceCaller); if (SUCCEEDED(hr)) { hr = calleeHostScriptContext->SetCaller((IUnknown*)sourceCaller, (IUnknown**)&previousSourceCaller); } if (SUCCEEDED(hr)) { wasCallerSet = TRUE; hr = calleeHostScriptContext->PushHostScriptContext(); } if (FAILED(hr)) { // CONSIDER: Should this be callerScriptContext if we failed? JavascriptError::MapAndThrowError(targetScriptContext, hr); } wasDispatchExCallerPushed = TRUE; result = JavascriptFunction::CallFunction<true>(function, entryPoint, args, true /*useLargeArgCount*/); ScriptContext* callerScriptContext = callerHostScriptContext->GetScriptContext(); result = CrossSite::MarshalVar(callerScriptContext, result); }, [&](bool hasException) { if (sourceCaller != nullptr) { callerHostScriptContext->ReleaseDispatchExCaller(sourceCaller); } IUnknown* originalCaller = nullptr; if (wasDispatchExCallerPushed) { calleeHostScriptContext->PopHostScriptContext(); } if (wasCallerSet) { calleeHostScriptContext->SetCaller(previousSourceCaller, &originalCaller); if (previousSourceCaller) { previousSourceCaller->Release(); } if (originalCaller) { originalCaller->Release(); } } }); Assert(result != nullptr); return result; }
void JavascriptExternalFunction::PrepareExternalCall(Js::Arguments * args) { ScriptContext * scriptContext = this->type->GetScriptContext(); Assert(!scriptContext->GetThreadContext()->IsDisableImplicitException()); scriptContext->VerifyAlive(); Assert(scriptContext->GetThreadContext()->IsScriptActive()); if (args->Info.Count == 0) { JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined); } Var &thisVar = args->Values[0]; Js::TypeId typeId = Js::JavascriptOperators::GetTypeId(thisVar); this->callCount++; if (IS_JS_ETW(EventEnabledJSCRIPT_HOSTING_EXTERNAL_FUNCTION_CALL_START())) { JavascriptFunction* caller = nullptr; // Lot the caller function if the call count of the external function pass certain threshold (randomly pick 256) // we don't want to call stackwalk too often. The threshold can be adjusted as needed. if (callCount >= ETW_MIN_COUNT_FOR_CALLER && ((callCount % ETW_MIN_COUNT_FOR_CALLER) == 0)) { Js::JavascriptStackWalker stackWalker(scriptContext); bool foundScriptCaller = false; while(stackWalker.GetCaller(&caller)) { if(caller != nullptr && Js::ScriptFunction::Is(caller)) { foundScriptCaller = true; break; } } if(foundScriptCaller) { Var sourceString = caller->EnsureSourceString(); Assert(JavascriptString::Is(sourceString)); const char16* callerString = Js::JavascriptString::FromVar(sourceString)->GetSz(); char16* outString = (char16*)callerString; int length = 0; if (wcschr(callerString, _u('\n')) != NULL || wcschr(callerString, _u('\n')) != NULL) { length = Js::JavascriptString::FromVar(sourceString)->GetLength(); outString = HeapNewArray(char16, length+1); int j = 0; for (int i = 0; i < length; i++) { if (callerString[i] != _u('\n') && callerString[i] != L'\r') { outString[j++] = callerString[i]; } } outString[j] = L'\0'; } JS_ETW(EventWriteJSCRIPT_HOSTING_CALLER_TO_EXTERNAL(scriptContext, this, typeId, outString, callCount)); if (outString != callerString) { HeapDeleteArray(length+1, outString); } #if DBG_DUMP if (Js::Configuration::Global.flags.Trace.IsEnabled(Js::HostPhase)) { Output::Print(_u("Large number of Call to trampoline: methodAddr= %p, Object typeid= %d, caller method= %s, callcount= %d\n"), this, typeId, callerString, callCount); } #endif } } JS_ETW(EventWriteJSCRIPT_HOSTING_EXTERNAL_FUNCTION_CALL_START(scriptContext, this, typeId)); #if DBG_DUMP if (Js::Configuration::Global.flags.Trace.IsEnabled(Js::HostPhase)) { Output::Print(_u("Call to trampoline: methodAddr= %p, Object typeid= %d\n"), this, typeId); } #endif } Js::RecyclableObject* directHostObject = nullptr; switch(typeId) { case TypeIds_Integer: #if FLOATVAR case TypeIds_Number: #endif // FLOATVAR Assert(!Js::RecyclableObject::Is(thisVar)); break; default: { Assert(Js::RecyclableObject::Is(thisVar)); ScriptContext* scriptContextThisVar = Js::RecyclableObject::FromVar(thisVar)->GetScriptContext(); // We need to verify "this" pointer is active as well. The problem is that DOM prototype functions are // the same across multiple frames, and caller can do function.call(closedthis) Assert(!scriptContext->GetThreadContext()->IsDisableImplicitException()); scriptContextThisVar->VerifyAlive(); // translate direct host for fastDOM. switch(typeId) { case Js::TypeIds_GlobalObject: { Js::GlobalObject* srcGlobalObject = static_cast<Js::GlobalObject*>(thisVar); directHostObject = srcGlobalObject->GetDirectHostObject(); // For jsrt, direct host object can be null. If thats the case don't change it. if (directHostObject != nullptr) { thisVar = directHostObject; } } break; case Js::TypeIds_Undefined: case Js::TypeIds_Null: { // Call to DOM function with this as "undefined" or "null" // This should be converted to Global object Js::GlobalObject* srcGlobalObject = scriptContextThisVar->GetGlobalObject() ; directHostObject = srcGlobalObject->GetDirectHostObject(); // For jsrt, direct host object can be null. If thats the case don't change it. if (directHostObject != nullptr) { thisVar = directHostObject; } } break; } } break; } }
Js::Var WabtInterface::EntryConvertWast2Wasm(RecyclableObject* function, CallInfo callInfo, ...) { ScriptContext* scriptContext = function->GetScriptContext(); PROBE_STACK(function->GetScriptContext(), Constants::MinStackDefault); ARGUMENTS(args, callInfo); AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'"); Assert(!(callInfo.Flags & CallFlags_New)); if (args.Info.Count < 2 || !JavascriptString::Is(args[1])) { JavascriptError::ThrowTypeError(scriptContext, WASMERR_NeedBufferSource); } bool isSpecText = false; if (args.Info.Count > 2) { // optional config object if (!JavascriptOperators::IsObject(args[2])) { JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedObject, _u("config")); } DynamicObject * configObject = JavascriptObject::FromVar(args[2]); Js::Var isSpecVar = JavascriptOperators::OP_GetProperty(configObject, PropertyIds::spec, scriptContext); isSpecText = JavascriptConversion::ToBool(isSpecVar, scriptContext); } ArenaAllocator arena(_u("Wast2Wasm"), scriptContext->GetThreadContext()->GetPageAllocator(), Throw::OutOfMemory); Context context; size_t wastSize; char* wastBuffer = nullptr; ENTER_PINNED_SCOPE(JavascriptString, string); string = (JavascriptString*)args[1]; const char16* str = string->GetString(); context.allocator = &arena; context.scriptContext = scriptContext; size_t origSize = string->GetLength(); auto allocator = [&arena](size_t size) {return (utf8char_t*)AnewArray(&arena, byte, size);}; utf8::WideStringToNarrow(allocator, str, origSize, &wastBuffer, &wastSize); LEAVE_PINNED_SCOPE(); // string try { ChakraWabt::SpecContext spec; ChakraWabt::ChakraContext wabtCtx; wabtCtx.user_data = &context; wabtCtx.createBuffer = CreateBuffer; wabtCtx.features.sign_extends = CONFIG_FLAG(WasmSignExtends); if (isSpecText) { wabtCtx.spec = &spec; spec.setProperty = SetProperty; spec.int32ToVar = Int32ToVar; spec.int64ToVar = Int64ToVar; spec.stringToVar = StringToVar; spec.createObject = CreateObject; spec.createArray = CreateArray; spec.push = Push; } void* result = ChakraWabt::ConvertWast2Wasm(wabtCtx, wastBuffer, (uint)wastSize, isSpecText); if (result == nullptr) { return scriptContext->GetLibrary()->GetUndefined(); } return result; } catch (ChakraWabt::Error& e) { JavascriptError::ThrowTypeErrorVar(scriptContext, WABTERR_WabtError, NarrowStringToWide(&context, e.message)); } }
void ModuleNamespace::Initialize() { ScriptContext* scriptContext = moduleRecord->GetRealm()->GetScriptContext(); Recycler* recycler = scriptContext->GetRecycler(); SourceTextModuleRecord* sourceTextModuleRecord = static_cast<SourceTextModuleRecord*>(moduleRecord); JavascriptLibrary* library = GetLibrary(); if (scriptContext->GetConfig()->IsES6ToStringTagEnabled()) { DynamicObject::SetPropertyWithAttributes(PropertyIds::_symbolToStringTag, library->GetModuleTypeDisplayString(), PropertyConfigurable, nullptr); } DynamicType* type = library->CreateFunctionWithLengthType(&EntryInfo::SymbolIterator); RuntimeFunction* iteratorFunction = RecyclerNewEnumClass(scriptContext->GetRecycler(), library->EnumFunctionClass, RuntimeFunction, type, &EntryInfo::SymbolIterator); DynamicObject::SetPropertyWithAttributes(PropertyIds::_symbolIterator, iteratorFunction, PropertyBuiltInMethodDefaults, nullptr); ModuleImportOrExportEntryList* localExportList = sourceTextModuleRecord->GetLocalExportEntryList(); // We don't have a type handler that can handle ModuleNamespace object. We have properties that could be aliased // like {export foo as foo1, foo2, foo3}, and external properties as reexport from current module. The problem with aliasing // is that multiple propertyId can be associated with one slotIndex. We need to build from PropertyMap directly here. // there is one instance of ModuleNamespace per module file; we can always use the BigPropertyIndex for security. propertyMap = RecyclerNew(recycler, SimplePropertyDescriptorMap, recycler, sourceTextModuleRecord->GetLocalExportCount()); if (localExportList != nullptr) { localExportList->Map([=](ModuleImportOrExportEntry exportEntry) { PropertyId exportNameId = exportEntry.exportName->GetPropertyId(); PropertyId localNameId = exportEntry.localName->GetPropertyId(); const Js::PropertyRecord* exportNameRecord = scriptContext->GetThreadContext()->GetPropertyName(exportNameId); ModuleNameRecord* importRecord = nullptr; AssertMsg(exportNameId != Js::Constants::NoProperty, "should have been initialized already"); // ignore local exports that are actually indirect exports. if (sourceTextModuleRecord->GetImportEntryList() == nullptr || (sourceTextModuleRecord->ResolveImport(localNameId, &importRecord) && importRecord == nullptr)) { BigPropertyIndex index = sourceTextModuleRecord->GetLocalExportSlotIndexByExportName(exportNameId); Assert((uint)index < sourceTextModuleRecord->GetLocalExportCount()); SimpleDictionaryPropertyDescriptor<BigPropertyIndex> propertyDescriptor = { index, PropertyModuleNamespaceDefault }; propertyMap->Add(exportNameRecord, propertyDescriptor); } }); } // update the local slot to use the storage for local exports. SetNSSlotsForModuleNS(sourceTextModuleRecord->GetLocalExportSlots()); // For items that are not in the local export list, we need to resolve them to get it ExportedNames* exportedNames = sourceTextModuleRecord->GetExportedNames(nullptr); ModuleNameRecord* moduleNameRecord = nullptr; #if DBG uint unresolvableExportsCount = 0; uint localExportCount = 0; #endif if (exportedNames != nullptr) { exportedNames->Map([&](PropertyId propertyId) { if (!moduleRecord->ResolveExport(propertyId, nullptr, nullptr, &moduleNameRecord)) { // ignore ambigious resolution. #if DBG unresolvableExportsCount++; #endif return; } // non-ambiguous resolution. if (moduleNameRecord == nullptr) { JavascriptError::ThrowSyntaxError(scriptContext, JSERR_ResolveExportFailed, scriptContext->GetPropertyName(propertyId)->GetBuffer()); } if (moduleNameRecord->module == moduleRecord) { // skip local exports as they are covered in the localExportSlots. #if DBG localExportCount++; #endif return; } Assert(moduleNameRecord->module != moduleRecord); this->AddUnambiguousNonLocalExport(propertyId, moduleNameRecord); }); } #if DBG uint totalExportCount = exportedNames != nullptr ? exportedNames->Count() : 0; uint unambiguousNonLocalCount = (this->GetUnambiguousNonLocalExports() != nullptr) ? this->GetUnambiguousNonLocalExports()->Count() : 0; Assert(totalExportCount == localExportCount + unambiguousNonLocalCount + unresolvableExportsCount); #endif BOOL result = this->PreventExtensions(); Assert(result); }
// SharedArrayBuffer.prototype.slice Var SharedArrayBuffer::EntrySlice(RecyclableObject* function, CallInfo callInfo, ...) { PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault); ARGUMENTS(args, callInfo); ScriptContext* scriptContext = function->GetScriptContext(); AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'"); Assert(!(callInfo.Flags & CallFlags_New)); if (!SharedArrayBuffer::Is(args[0])) { JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedSharedArrayBufferObject); } JavascriptLibrary* library = scriptContext->GetLibrary(); SharedArrayBuffer* currentBuffer = SharedArrayBuffer::FromVar(args[0]); int64 currentLen = (int64)currentBuffer->GetByteLength(); int64 start = 0, end = 0; int64 newLen = 0; // If no start or end arguments, use the entire length if (args.Info.Count < 2) { newLen = currentLen; } else { start = JavascriptArray::GetIndexFromVar(args[1], currentLen, scriptContext); // If no end argument, use length as the end if (args.Info.Count < 3 || args[2] == library->GetUndefined()) { end = currentLen; } else { end = JavascriptArray::GetIndexFromVar(args[2], currentLen, scriptContext); } newLen = end > start ? end - start : 0; } // We can't have allocated an SharedArrayBuffer with byteLength > MaxArrayBufferLength. // start and end are clamped to valid indices, so the new length also cannot exceed MaxArrayBufferLength. // Therefore, should be safe to cast down newLen to uint32. Assert(newLen < MaxSharedArrayBufferLength); uint32 newbyteLength = static_cast<uint32>(newLen); SharedArrayBuffer* newBuffer = nullptr; if (scriptContext->GetConfig()->IsES6SpeciesEnabled()) { JavascriptFunction* defaultConstructor = scriptContext->GetLibrary()->GetSharedArrayBufferConstructor(); RecyclableObject* constructor = JavascriptOperators::SpeciesConstructor(currentBuffer, defaultConstructor, scriptContext); AssertOrFailFast(JavascriptOperators::IsConstructor(constructor)); bool isDefaultConstructor = constructor == defaultConstructor; Js::Var newVar = JavascriptOperators::NewObjectCreationHelper_ReentrancySafe(constructor, isDefaultConstructor, scriptContext->GetThreadContext(), [=]()->Js::Var { Js::Var constructorArgs[] = { constructor, JavascriptNumber::ToVar(newbyteLength, scriptContext) }; Js::CallInfo constructorCallInfo(Js::CallFlags_New, _countof(constructorArgs)); return JavascriptOperators::NewScObject(constructor, Js::Arguments(constructorCallInfo, constructorArgs), scriptContext); }); if (!SharedArrayBuffer::Is(newVar)) { JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedSharedArrayBufferObject); } newBuffer = SharedArrayBuffer::FromVar(newVar); if (newBuffer == currentBuffer) { JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedSharedArrayBufferObject); } if (newBuffer->GetByteLength() < newbyteLength) { JavascriptError::ThrowTypeError(scriptContext, JSERR_ArgumentOutOfRange, _u("SharedArrayBuffer.prototype.slice")); } } else { newBuffer = library->CreateSharedArrayBuffer(newbyteLength); } Assert(newBuffer); Assert(newBuffer->GetByteLength() >= newbyteLength); // Don't bother doing memcpy if we aren't copying any elements if (newbyteLength > 0) { AssertMsg(currentBuffer->GetBuffer() != nullptr, "buffer must not be null when we copy from it"); js_memcpy_s(newBuffer->GetBuffer(), newbyteLength, currentBuffer->GetBuffer() + start, newbyteLength); } return newBuffer; }