RecyclableObject* ForInObjectEnumerator::GetFirstPrototypeWithEnumerableProperties(RecyclableObject* object) { RecyclableObject* firstPrototype = nullptr; if (JavascriptOperators::GetTypeId(object) != TypeIds_HostDispatch) { firstPrototype = object; while (true) { firstPrototype = firstPrototype->GetPrototype(); if (JavascriptOperators::GetTypeId(firstPrototype) == TypeIds_Null) { firstPrototype = nullptr; break; } if (!DynamicType::Is(firstPrototype->GetTypeId()) || !DynamicObject::FromVar(firstPrototype)->GetHasNoEnumerableProperties()) { break; } } } return firstPrototype; }
DynamicObject* DynamicObject::FromVar(Var aValue) { RecyclableObject* obj = RecyclableObject::FromVar(aValue); AssertMsg(obj->DbgIsDynamicObject(), "Ensure instance is actually a DynamicObject"); Assert(DynamicType::Is(obj->GetTypeId())); return static_cast<DynamicObject*>(obj); }
Var JavascriptReflect::EntryPreventExtensions(RecyclableObject* function, CallInfo callInfo, ...) { PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault); ARGUMENTS(args, callInfo); ScriptContext* scriptContext = function->GetScriptContext(); AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, _u("Reflect.preventExtensions")); AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'"); if (args.Info.Flags & CallFlags_New) { JavascriptError::ThrowTypeError(scriptContext, JSERR_ErrorOnNew, _u("Reflect.preventExtensions")); } if (args.Info.Count < 2 || !JavascriptOperators::IsObject(args[1])) { JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedObject, _u("Reflect.preventExtensions")); } Var target = args[1]; RecyclableObject* targetObj = RecyclableObject::FromVar(target); GlobalObject* globalObject = targetObj->GetLibrary()->GetGlobalObject(); if (globalObject != targetObj && globalObject && (globalObject->ToThis() == targetObj)) { globalObject->PreventExtensions(); } return scriptContext->GetLibrary()->GetTrueOrFalse(targetObj->PreventExtensions()); }
BOOL DynamicObject::CallToPrimitiveFunction(Var toPrimitiveFunction, PropertyId propertyId, Var* result, ScriptContext * requestContext) { if (JavascriptConversion::IsCallable(toPrimitiveFunction)) { RecyclableObject* toStringFunction = RecyclableObject::FromVar(toPrimitiveFunction); ThreadContext * threadContext = requestContext->GetThreadContext(); Var aResult = threadContext->ExecuteImplicitCall(toStringFunction, ImplicitCall_ToPrimitive, [=]() -> Js::Var { // Stack object should have a pre-op bail on implicit call. We shouldn't see them here. Assert(!ThreadContext::IsOnStack(this) || threadContext->HasNoSideEffect(toStringFunction)); return toStringFunction->GetEntryPoint()(toStringFunction, CallInfo(CallFlags_Value, 1), this); }); if (!aResult) { // There was an implicit call and implicit calls are disabled. This would typically cause a bailout. Assert(threadContext->IsDisableImplicitCall()); *result = requestContext->GetLibrary()->GetNull(); return true; } if (JavascriptOperators::GetTypeId(aResult) <= TypeIds_LastToPrimitiveType) { *result = aResult; return true; } } return false; }
Var CrossSite::MarshalFrameDisplay(ScriptContext* scriptContext, FrameDisplay *display) { TTD_XSITE_LOG(scriptContext, "MarshalFrameDisplay", nullptr); uint16 length = display->GetLength(); FrameDisplay *newDisplay = RecyclerNewPlus(scriptContext->GetRecycler(), length * sizeof(Var), FrameDisplay, length); for (uint16 i = 0; i < length; i++) { Var value = display->GetItem(i); if (WithScopeObject::Is(value)) { // Here we are marshalling the wrappedObject and then ReWrapping th object in the new context. RecyclableObject* wrappedObject = WithScopeObject::FromVar(value)->GetWrappedObject(); ScriptContext* wrappedObjectScriptContext = wrappedObject->GetScriptContext(); value = JavascriptOperators::ToWithObject(CrossSite::MarshalVar(scriptContext, wrappedObject, wrappedObjectScriptContext), scriptContext); } else { value = CrossSite::MarshalVar(scriptContext, value); } newDisplay->SetItem(i, value); } return (Var)newDisplay; }
BOOL ForInObjectEnumerator::InitializeCurrentEnumerator(RecyclableObject * object, ForInCache * forInCache) { EnumeratorFlags flags = enumerator.GetFlags(); RecyclableObject * prototype = object->GetPrototype(); if (prototype == nullptr || prototype->GetTypeId() == TypeIds_Null) { // If this is the last object on the prototype chain, we don't need to get the non-enumerable properties any more to track shadowing flags &= ~EnumeratorFlags::EnumNonEnumerable; } return InitializeCurrentEnumerator(object, flags, GetScriptContext(), forInCache); }
BOOL CrossSite::NeedMarshalVar(Var instance, ScriptContext * requestContext) { if (TaggedNumber::Is(instance)) { return FALSE; } RecyclableObject * object = RecyclableObject::UnsafeFromVar(instance); if (object->GetScriptContext() == requestContext) { return FALSE; } if (DynamicType::Is(object->GetTypeId())) { return !DynamicObject::UnsafeFromVar(object)->IsCrossSiteObject() && !object->IsExternal(); } return TRUE; }
void CrossSite::MarshalPrototypeChain(ScriptContext* scriptContext, DynamicObject * object) { RecyclableObject * prototype = object->GetPrototype(); while (prototype->GetTypeId() != TypeIds_Null && prototype->GetTypeId() != TypeIds_HostDispatch) { // We should not see any static type or host dispatch here DynamicObject * prototypeObject = DynamicObject::FromVar(prototype); if (prototypeObject->IsCrossSiteObject()) { break; } if (scriptContext != prototypeObject->GetScriptContext() && !prototypeObject->IsExternal()) { MarshalDynamicObject(scriptContext, prototypeObject); } prototype = prototypeObject->GetPrototype(); } }
Var JavascriptWeakMap::NewInstance(RecyclableObject* function, CallInfo callInfo, ...) { PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault); ARGUMENTS(args, callInfo); ScriptContext* scriptContext = function->GetScriptContext(); JavascriptLibrary* library = scriptContext->GetLibrary(); Var newTarget = callInfo.Flags & CallFlags_NewTarget ? args.Values[args.Info.Count] : args[0]; bool isCtorSuperCall = (callInfo.Flags & CallFlags_New) && newTarget != nullptr && RecyclableObject::Is(newTarget); Assert(isCtorSuperCall || !(callInfo.Flags & CallFlags_New) || args[0] == nullptr); CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(WeakMapCount); JavascriptWeakMap* weakMapObject = nullptr; if (callInfo.Flags & CallFlags_New) { weakMapObject = library->CreateWeakMap(); } else { JavascriptError::ThrowTypeErrorVar(scriptContext, JSERR_NeedObjectOfType, _u("WeakMap"), _u("WeakMap")); } Assert(weakMapObject != nullptr); Var iterable = (args.Info.Count > 1) ? args[1] : library->GetUndefined(); RecyclableObject* iter = nullptr; RecyclableObject* adder = nullptr; if (JavascriptConversion::CheckObjectCoercible(iterable, scriptContext)) { iter = JavascriptOperators::GetIterator(iterable, scriptContext); Var adderVar = JavascriptOperators::GetProperty(weakMapObject, PropertyIds::set, scriptContext); if (!JavascriptConversion::IsCallable(adderVar)) { JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedFunction); } adder = RecyclableObject::FromVar(adderVar); } if (iter != nullptr) { Var nextItem; Var undefined = library->GetUndefined(); while (JavascriptOperators::IteratorStepAndValue(iter, scriptContext, &nextItem)) { if (!JavascriptOperators::IsObject(nextItem)) { JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedObject); } RecyclableObject* obj = RecyclableObject::FromVar(nextItem); Var key, value; if (!JavascriptOperators::GetItem(obj, 0u, &key, scriptContext)) { key = undefined; } if (!JavascriptOperators::GetItem(obj, 1u, &value, scriptContext)) { value = undefined; } adder->GetEntryPoint()(adder, CallInfo(CallFlags_Value, 3), weakMapObject, key, value); } } return isCtorSuperCall ? JavascriptOperators::OrdinaryCreateFromConstructor(RecyclableObject::FromVar(newTarget), weakMapObject, nullptr, scriptContext) : weakMapObject; }
Var BoundFunction::NewInstance(RecyclableObject* function, CallInfo callInfo, ...) { RUNTIME_ARGUMENTS(args, callInfo); ScriptContext* scriptContext = function->GetScriptContext(); if (args.Info.Count == 0) { JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedFunction /* TODO-ERROR: get arg name - args[0] */); } BoundFunction *boundFunction = (BoundFunction *) function; Var targetFunction = boundFunction->targetFunction; // // var o = new boundFunction() // a new object should be created using the actual function object // Var newVarInstance = nullptr; if (callInfo.Flags & CallFlags_New) { if (JavascriptProxy::Is(targetFunction)) { JavascriptProxy* proxy = JavascriptProxy::FromVar(targetFunction); Arguments proxyArgs(CallInfo(CallFlags_New, 1), &targetFunction); args.Values[0] = newVarInstance = proxy->ConstructorTrap(proxyArgs, scriptContext, 0); } else { args.Values[0] = newVarInstance = JavascriptOperators::NewScObjectNoCtor(targetFunction, scriptContext); } } Js::Arguments actualArgs = args; if (boundFunction->count > 0) { BOOL isCrossSiteObject = boundFunction->IsCrossSiteObject(); // OACR thinks that this can change between here and the check in the for loop below const unsigned int argCount = args.Info.Count; if ((boundFunction->count + argCount) > CallInfo::kMaxCountArgs) { JavascriptError::ThrowRangeError(scriptContext, JSERR_ArgListTooLarge); } Var *newValues = RecyclerNewArray(scriptContext->GetRecycler(), Var, boundFunction->count + argCount); uint index = 0; // // For [[Construct]] use the newly created var instance // For [[Call]] use the "this" to which bind bound it. // if (callInfo.Flags & CallFlags_New) { newValues[index++] = args[0]; } else { newValues[index++] = boundFunction->boundThis; } // Copy the bound args if (!isCrossSiteObject) { for (uint i = 0; i < boundFunction->count; i++) { newValues[index++] = boundFunction->boundArgs[i]; } } else { // it is possible that the bound arguments are not marshalled yet. for (uint i = 0; i < boundFunction->count; i++) { newValues[index++] = CrossSite::MarshalVar(scriptContext, boundFunction->boundArgs[i]); } } // Copy the extra args for (uint i=1; i<argCount; i++) { newValues[index++] = args[i]; } actualArgs = Arguments(args.Info, newValues); actualArgs.Info.Count = boundFunction->count + argCount; } else { if (!(callInfo.Flags & CallFlags_New)) { actualArgs.Values[0] = boundFunction->boundThis; } } RecyclableObject* actualFunction = RecyclableObject::FromVar(targetFunction); Var aReturnValue = JavascriptFunction::CallFunction<true>(actualFunction, actualFunction->GetEntryPoint(), actualArgs); // // [[Construct]] and call returned a non-object // return the newly created var instance // if ((callInfo.Flags & CallFlags_New) && !JavascriptOperators::IsObject(aReturnValue)) { aReturnValue = newVarInstance; } return aReturnValue; }
JavascriptString * ForInObjectEnumerator::MoveAndGetNext(PropertyId& propertyId) { PropertyRecord const * propRecord; PropertyAttributes attributes = PropertyNone; while (true) { propertyId = Constants::NoProperty; JavascriptString * currentIndex = enumerator.MoveAndGetNext(propertyId, &attributes); // The object type may have changed and we may not be able to use Jit fast path anymore. // canUseJitFastPath is determined in ForInObjectEnumerator::Initialize, once we decide we can't use // Jit fast path we will never go back to use fast path so && with current value - if it's already // false we don't call CanUseJITFastPath() this->canUseJitFastPath = this->canUseJitFastPath && enumerator.CanUseJITFastPath(); if (currentIndex) { if (this->shadowData == nullptr) { // There are no prototype that has enumerable properties, // don't need to keep track of the propertyIds we visited. // We have asked for enumerable properties only, so don't need to check the attribute returned. Assert(attributes & PropertyEnumerable); return currentIndex; } // Property Id does not exist. if (propertyId == Constants::NoProperty) { if (VirtualTableInfo<Js::PropertyString>::HasVirtualTable(currentIndex)) { // If we have a property string, it is assumed that the propertyId is being // kept alive with the object PropertyString * propertyString = (PropertyString *)currentIndex; propertyId = propertyString->GetPropertyRecord()->GetPropertyId(); } else { ScriptContext* scriptContext = currentIndex->GetScriptContext(); scriptContext->GetOrAddPropertyRecord(currentIndex->GetString(), currentIndex->GetLength(), &propRecord); propertyId = propRecord->GetPropertyId(); // We keep the track of what is enumerated using a bit vector of propertyID. // so the propertyId can't be collected until the end of the for in enumerator // Keep a list of the property string. this->shadowData->newPropertyStrings.Prepend(GetScriptContext()->GetRecycler(), propRecord); } } if (TestAndSetEnumerated(propertyId) //checks if the property is already enumerated or not && (attributes & PropertyEnumerable)) { return currentIndex; } } else { if (this->shadowData == nullptr) { Assert(!this->enumeratingPrototype); return nullptr; } RecyclableObject * object; if (!this->enumeratingPrototype) { this->enumeratingPrototype = true; object = this->shadowData->firstPrototype; this->shadowData->currentObject = object; } else { //walk the prototype chain object = this->shadowData->currentObject->GetPrototype(); this->shadowData->currentObject = object; if ((object == nullptr) || (JavascriptOperators::GetTypeId(object) == TypeIds_Null)) { return nullptr; } } do { if (!InitializeCurrentEnumerator(object)) { return nullptr; } if (!enumerator.IsNullEnumerator()) { break; } //walk the prototype chain object = object->GetPrototype(); this->shadowData->currentObject = object; if ((object == nullptr) || (JavascriptOperators::GetTypeId(object) == TypeIds_Null)) { return nullptr; } } while (true); } } }