Var JavascriptError::NewWinRTErrorInstance(RecyclableObject* function, CallInfo callInfo, ...) { PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault); ARGUMENTS(args, callInfo); ScriptContext* scriptContext = function->GetScriptContext(); JavascriptError* pError = scriptContext->GetHostScriptContext()->CreateWinRTError(nullptr, nullptr); return JavascriptError::NewInstance(function, pError, callInfo, args); }
Var JavascriptError::NewWinRTErrorInstance(RecyclableObject* function, CallInfo callInfo, ...) { PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault); ARGUMENTS(args, callInfo); ScriptContext* scriptContext = function->GetScriptContext(); JavascriptError* pError = scriptContext->GetHostScriptContext()->CreateWinRTError(nullptr, nullptr); Var newTarget = callInfo.Flags & CallFlags_NewTarget ? args.Values[args.Info.Count] : args[0]; Var message = args.Info.Count > 1 ? args[1] : scriptContext->GetLibrary()->GetUndefined(); return JavascriptError::NewInstance(function, pError, callInfo, newTarget, message); }
Var DataView::NewInstance(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'"); Var newTarget = callInfo.Flags & CallFlags_NewTarget ? args.Values[args.Info.Count] : args[0]; bool isCtorSuperCall = (callInfo.Flags & CallFlags_New) && newTarget != nullptr && RecyclableObject::Is(newTarget); Assert(isCtorSuperCall || !(callInfo.Flags & CallFlags_New) || args[0] == nullptr); uint32 byteLength = 0; uint32 mappedLength; int32 offset = 0; double numberOffset = 0; ArrayBuffer* arrayBuffer = nullptr; DataView* dataView; //1. If NewTarget is undefined, throw a TypeError exception. if (!(callInfo.Flags & CallFlags_New) || (newTarget && JavascriptOperators::IsUndefinedObject(newTarget))) { JavascriptError::ThrowTypeError(scriptContext, JSERR_ClassConstructorCannotBeCalledWithoutNew, L"DataView"); } if (args.Info.Count < 2) { JavascriptError::ThrowTypeError(scriptContext, JSERR_DataView_NeedArgument, L"buffer"); } // Currently the only reason we check for an external object is projection related, so it remains under conditional compilation. RecyclableObject* jsArraySource = NULL; if (JavascriptOperators::IsObject(args[1]) && JavascriptConversion::ToObject(args[1], scriptContext, &jsArraySource)) { HRESULT hr = scriptContext->GetHostScriptContext()->ArrayBufferFromExternalObject(jsArraySource, &arrayBuffer); switch (hr) { case S_OK: case S_FALSE: // Both of these cases will be handled by the arrayBuffer null check. break; default: // Any FAILURE HRESULT or unexpected HRESULT. JavascriptError::ThrowTypeError(scriptContext, JSERR_DataView_InvalidArugment, L"buffer"); break; } } //2. If Type(buffer) is not Object, throw a TypeError exception. //3. If buffer does not have an [[ArrayBufferData]] internal slot, throw a TypeError exception. if (arrayBuffer == nullptr) { if (ArrayBuffer::Is(args[1])) { arrayBuffer = ArrayBuffer::FromVar(args[1]); } else { JavascriptError::ThrowTypeError(scriptContext, JSERR_DataView_NeedArgument, L"buffer"); } } //4. Let numberOffset be ToNumber(byteOffset). //5. Let offset be ToInteger(numberOffset). //6. ReturnIfAbrupt(offset). //7. If numberOffset <> offset or offset < 0, throw a RangeError exception. if (args.Info.Count > 2) { Var secondArgument = args[2]; numberOffset = JavascriptConversion::ToNumber(secondArgument, scriptContext); offset = JavascriptConversion::ToInt32(numberOffset); if (offset < 0 || numberOffset != offset) { JavascriptError::ThrowRangeError( scriptContext, JSERR_DataView_InvalidArugment, L"byteOffset"); } } //8. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. if (arrayBuffer->IsDetached()) { JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedDataView); } //9. Let bufferByteLength be the value of buffer's[[ArrayBufferByteLength]] internal slot. //10. If offset > bufferByteLength, throw a RangeError exception. byteLength = arrayBuffer->GetByteLength(); if ((uint32)offset > byteLength) { JavascriptError::ThrowRangeError( scriptContext, JSERR_DataView_InvalidArugment, L"byteOffset"); } //11. If byteLength is undefined, then // a. Let viewByteLength be bufferByteLength - offset. //12. Else, // a. Let viewByteLength be ToLength(byteLength). // b. ReturnIfAbrupt(viewLength). // c. If offset + viewByteLength > bufferByteLength, throw a RangeError exception. if (args.Info.Count > 3 && !JavascriptOperators::IsUndefinedObject(args[3])) { Var thirdArgument = args[3]; mappedLength = (uint32)JavascriptConversion::ToLength(thirdArgument, scriptContext); uint32 viewRange = mappedLength + offset; if (viewRange > byteLength || viewRange < mappedLength) // overflow indicates out-of-range { JavascriptError::ThrowRangeError( scriptContext, JSERR_DataView_InvalidArugment, L"byteLength"); } } else { mappedLength = byteLength - offset; } //13. Let O be OrdinaryCreateFromConstructor(NewTarget, "%DataViewPrototype%", [[DataView]], [[ViewedArrayBuffer]], [[ByteLength]], [[ByteOffset]]). //14. ReturnIfAbrupt(O). //15. Set O's[[DataView]] internal slot to true. //16. Set O's[[ViewedArrayBuffer]] internal slot to buffer. //17. Set O's[[ByteLength]] internal slot to viewByteLength. //18. Set O's[[ByteOffset]] internal slot to offset. //19. Return O. dataView = scriptContext->GetLibrary()->CreateDataView(arrayBuffer, offset, mappedLength); return isCtorSuperCall ? JavascriptOperators::OrdinaryCreateFromConstructor(RecyclableObject::FromVar(newTarget), dataView, nullptr, scriptContext) : dataView; }
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; }