diff --git a/lib/Runtime/Debug/DiagObjectModel.cpp b/lib/Runtime/Debug/DiagObjectModel.cpp index bc2ae4f89f3..ba17a095df2 100644 --- a/lib/Runtime/Debug/DiagObjectModel.cpp +++ b/lib/Runtime/Debug/DiagObjectModel.cpp @@ -2547,7 +2547,6 @@ namespace Js // We need to special-case RegExp constructor here because it has some special properties (above) and some // special enumerable properties which should all show up in the debugger. JavascriptRegExpConstructor* regExp = scriptContext->GetLibrary()->GetRegExpConstructor(); - Js::JavascriptFunction* jsFunction = Js::JavascriptFunction::FromVar(object); if (regExp == object) { @@ -2563,11 +2562,6 @@ namespace Js InsertItem(originalObject, object, propertyId, isConst, isUnscoped, &pMethodsGroupWalker); } } - else if ((jsFunction->IsScriptFunction() && !jsFunction->GetFunctionProxy()->IsJsBuiltInCode()) || jsFunction->IsBoundFunction()) - { - // Adding special property length for the ScriptFunction, like it is done in JavascriptFunction::GetSpecialNonEnumerablePropertyName - InsertItem(originalObject, object, PropertyIds::length, true/*not editable*/, false /*isUnscoped*/, &pMethodsGroupWalker); - } } } @@ -4216,4 +4210,4 @@ namespace Js } #endif } -#endif \ No newline at end of file +#endif diff --git a/lib/Runtime/Library/BoundFunction.cpp b/lib/Runtime/Library/BoundFunction.cpp index 2416fc0d6af..44b4e18e9fb 100644 --- a/lib/Runtime/Library/BoundFunction.cpp +++ b/lib/Runtime/Library/BoundFunction.cpp @@ -24,7 +24,6 @@ namespace Js count(0), boundArgs(nullptr) { - DebugOnly(VerifyEntryPoint()); AssertMsg(args.Info.Count > 0, "wrong number of args in BoundFunction"); @@ -44,21 +43,19 @@ namespace Js } type->SetPrototype(proto); } + + int len = 0; // If targetFunction is proxy, need to make sure that traps are called in right order as per 19.2.3.2 in RC#4 dated April 3rd 2015. - // Here although we won't use value of length, this is just to make sure that we call traps involved with HasOwnProperty(Target, "length") and Get(Target, "length") - if (JavascriptProxy::Is(targetFunction)) + // additionally need to get the correct length value for the boundFunctions' length property + if (JavascriptOperators::HasOwnProperty(targetFunction, PropertyIds::length, scriptContext, nullptr) == TRUE) { - if (JavascriptOperators::HasOwnProperty(targetFunction, PropertyIds::length, scriptContext, nullptr) == TRUE) + Var varLength; + if (targetFunction->GetProperty(targetFunction, PropertyIds::length, &varLength, nullptr, scriptContext)) { - int len = 0; - Var varLength; - if (targetFunction->GetProperty(targetFunction, PropertyIds::length, &varLength, nullptr, scriptContext)) - { - len = JavascriptConversion::ToInt32(varLength, scriptContext); - } + len = JavascriptConversion::ToInt32(varLength, scriptContext); } - GetTypeHandler()->EnsureObjectReady(this); } + GetTypeHandler()->EnsureObjectReady(this); if (args.Info.Count > 1) { @@ -84,27 +81,12 @@ namespace Js // If no "this" is passed, "undefined" is used boundThis = scriptContext->GetLibrary()->GetUndefined(); } - } - BoundFunction::BoundFunction(RecyclableObject* targetFunction, Var boundThis, Var* args, uint argsCount, DynamicType * type) - : JavascriptFunction(type, &functionInfo), - count(argsCount), - boundArgs(nullptr) - { - DebugOnly(VerifyEntryPoint()); - - this->targetFunction = targetFunction; - this->boundThis = boundThis; - - if (argsCount != 0) - { - this->boundArgs = RecyclerNewArray(this->GetScriptContext()->GetRecycler(), Field(Var), argsCount); - - for (uint i = 0; i < argsCount; i++) - { - this->boundArgs[i] = args[i]; - } - } + // Reduce length number of bound args + len = len - this->count; + len = max(len, 0); + + SetPropertyWithAttributes(PropertyIds::length, TaggedInt::ToVarUnchecked(len), PropertyConfigurable, nullptr, PropertyOperation_None, SideEffects_None); } BoundFunction* BoundFunction::New(ScriptContext* scriptContext, ArgumentReader args) @@ -307,108 +289,11 @@ namespace Js return false; } - PropertyQueryFlags BoundFunction::HasPropertyQuery(PropertyId propertyId, _Inout_opt_ PropertyValueInfo* info) - { - if (propertyId == PropertyIds::length) - { - return PropertyQueryFlags::Property_Found; - } - - return JavascriptFunction::HasPropertyQuery(propertyId, info); - } - - PropertyQueryFlags BoundFunction::GetPropertyQuery(Var originalInstance, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext) - { - BOOL result; - if (GetPropertyBuiltIns(originalInstance, propertyId, value, info, requestContext, &result)) - { - return JavascriptConversion::BooleanToPropertyQueryFlags(result); - } - - return JavascriptFunction::GetPropertyQuery(originalInstance, propertyId, value, info, requestContext); - } - - PropertyQueryFlags BoundFunction::GetPropertyQuery(Var originalInstance, JavascriptString* propertyNameString, Var* value, PropertyValueInfo* info, ScriptContext* requestContext) - { - BOOL result; - PropertyRecord const* propertyRecord; - this->GetScriptContext()->FindPropertyRecord(propertyNameString, &propertyRecord); - - if (propertyRecord != nullptr && GetPropertyBuiltIns(originalInstance, propertyRecord->GetPropertyId(), value, info, requestContext, &result)) - { - return JavascriptConversion::BooleanToPropertyQueryFlags(result); - } - - return JavascriptFunction::GetPropertyQuery(originalInstance, propertyNameString, value, info, requestContext); - } - - bool BoundFunction::GetPropertyBuiltIns(Var originalInstance, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext, BOOL* result) - { - if (propertyId == PropertyIds::length) - { - // Get the "length" property of the underlying target function - int len = 0; - Var varLength; - if (targetFunction->GetProperty(targetFunction, PropertyIds::length, &varLength, nullptr, requestContext)) - { - len = JavascriptConversion::ToInt32(varLength, requestContext); - } - - // Reduce by number of bound args - len = len - this->count; - len = max(len, 0); - - *value = JavascriptNumber::ToVar(len, requestContext); - *result = true; - return true; - } - - return false; - } - PropertyQueryFlags BoundFunction::GetPropertyReferenceQuery(Var originalInstance, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext) { return BoundFunction::GetPropertyQuery(originalInstance, propertyId, value, info, requestContext); } - BOOL BoundFunction::SetProperty(PropertyId propertyId, Var value, PropertyOperationFlags flags, PropertyValueInfo* info) - { - BOOL result; - if (SetPropertyBuiltIns(propertyId, value, flags, info, &result)) - { - return result; - } - - return JavascriptFunction::SetProperty(propertyId, value, flags, info); - } - - BOOL BoundFunction::SetProperty(JavascriptString* propertyNameString, Var value, PropertyOperationFlags flags, PropertyValueInfo* info) - { - BOOL result; - PropertyRecord const* propertyRecord; - this->GetScriptContext()->FindPropertyRecord(propertyNameString, &propertyRecord); - - if (propertyRecord != nullptr && SetPropertyBuiltIns(propertyRecord->GetPropertyId(), value, flags, info, &result)) - { - return result; - } - - return JavascriptFunction::SetProperty(propertyNameString, value, flags, info); - } - - bool BoundFunction::SetPropertyBuiltIns(PropertyId propertyId, Var value, PropertyOperationFlags flags, PropertyValueInfo* info, BOOL* result) - { - if (propertyId == PropertyIds::length) - { - JavascriptError::ThrowCantAssignIfStrictMode(flags, this->GetScriptContext()); - - *result = false; - return true; - } - - return false; - } - _Check_return_ _Success_(return) BOOL BoundFunction::GetAccessors(PropertyId propertyId, _Outptr_result_maybenull_ Var* getter, _Outptr_result_maybenull_ Var* setter, ScriptContext* requestContext) { return DynamicObject::GetAccessors(propertyId, getter, setter, requestContext); @@ -429,56 +314,6 @@ namespace Js return SetProperty(propertyId, value, PropertyOperation_None, info); } - BOOL BoundFunction::DeleteProperty(PropertyId propertyId, PropertyOperationFlags flags) - { - if (propertyId == PropertyIds::length) - { - return false; - } - - return JavascriptFunction::DeleteProperty(propertyId, flags); - } - - BOOL BoundFunction::DeleteProperty(JavascriptString *propertyNameString, PropertyOperationFlags flags) - { - if (BuiltInPropertyRecords::length.Equals(propertyNameString)) - { - return false; - } - - return JavascriptFunction::DeleteProperty(propertyNameString, flags); - } - - BOOL BoundFunction::IsWritable(PropertyId propertyId) - { - if (propertyId == PropertyIds::length) - { - return false; - } - - return JavascriptFunction::IsWritable(propertyId); - } - - BOOL BoundFunction::IsConfigurable(PropertyId propertyId) - { - if (propertyId == PropertyIds::length) - { - return false; - } - - return JavascriptFunction::IsConfigurable(propertyId); - } - - BOOL BoundFunction::IsEnumerable(PropertyId propertyId) - { - if (propertyId == PropertyIds::length) - { - return false; - } - - return JavascriptFunction::IsEnumerable(propertyId); - } - BOOL BoundFunction::HasInstance(Var instance, ScriptContext* scriptContext, IsInstInlineCache* inlineCache) { return this->targetFunction->HasInstance(instance, scriptContext, inlineCache); diff --git a/lib/Runtime/Library/BoundFunction.h b/lib/Runtime/Library/BoundFunction.h index db788a818c6..dae83d87a10 100644 --- a/lib/Runtime/Library/BoundFunction.h +++ b/lib/Runtime/Library/BoundFunction.h @@ -19,31 +19,20 @@ namespace Js protected: BoundFunction(DynamicType * type); BoundFunction(Arguments args, DynamicType * type); - BoundFunction(RecyclableObject* targetFunction, Var boundThis, Var* args, uint argsCount, DynamicType *type); + public: static BoundFunction* New(ScriptContext* scriptContext, ArgumentReader args); static bool Is(Var func){ return JavascriptFunction::Is(func) && JavascriptFunction::UnsafeFromVar(func)->IsBoundFunction(); } static Var NewInstance(RecyclableObject* function, CallInfo callInfo, ...); virtual JavascriptString* GetDisplayNameImpl() const override; - virtual PropertyQueryFlags HasPropertyQuery(PropertyId propertyId, _Inout_opt_ PropertyValueInfo* info) override; - virtual PropertyQueryFlags GetPropertyQuery(Var originalInstance, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext) override; - virtual PropertyQueryFlags GetPropertyQuery(Var originalInstance, JavascriptString* propertyNameString, Var* value, PropertyValueInfo* info, ScriptContext* requestContext) override; virtual PropertyQueryFlags GetPropertyReferenceQuery(Var originalInstance, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext) override; - virtual BOOL SetProperty(PropertyId propertyId, Var value, PropertyOperationFlags flags, PropertyValueInfo* info) override; - virtual BOOL SetProperty(JavascriptString* propertyNameString, Var value, PropertyOperationFlags flags, PropertyValueInfo* info) override; _Check_return_ _Success_(return) virtual BOOL GetAccessors(PropertyId propertyId, _Outptr_result_maybenull_ Var* getter, _Outptr_result_maybenull_ Var* setter, ScriptContext* requestContext) override; virtual DescriptorFlags GetSetter(PropertyId propertyId, Var *setterValue, PropertyValueInfo* info, ScriptContext* requestContext) override; virtual DescriptorFlags GetSetter(JavascriptString* propertyNameString, Var *setterValue, PropertyValueInfo* info, ScriptContext* requestContext) override; virtual BOOL InitProperty(PropertyId propertyId, Var value, PropertyOperationFlags flags = PropertyOperation_None, PropertyValueInfo* info = NULL) override; - virtual BOOL DeleteProperty(PropertyId propertyId, PropertyOperationFlags flags) override; - virtual BOOL DeleteProperty(JavascriptString *propertyNameString, PropertyOperationFlags flags) override; - - virtual BOOL IsWritable(PropertyId propertyId) override; - virtual BOOL IsConfigurable(PropertyId propertyId) override; - virtual BOOL IsEnumerable(PropertyId propertyId) override; virtual BOOL HasInstance(Var instance, ScriptContext* scriptContext, IsInstInlineCache* inlineCache = NULL) override; virtual inline BOOL IsConstructor() const override; diff --git a/lib/Runtime/Library/JavascriptExternalFunction.cpp b/lib/Runtime/Library/JavascriptExternalFunction.cpp index 93b9dd54135..7e065ca928a 100644 --- a/lib/Runtime/Library/JavascriptExternalFunction.cpp +++ b/lib/Runtime/Library/JavascriptExternalFunction.cpp @@ -58,7 +58,7 @@ namespace Js bool __cdecl JavascriptExternalFunction::DeferredLengthInitializer(DynamicObject * instance, DeferredTypeHandlerBase * typeHandler, DeferredInitializeMode mode) { - Js::JavascriptLibrary::InitializeFunction(instance, typeHandler, mode); + Js::JavascriptLibrary::InitializeFunction(instance, typeHandler, mode); JavascriptExternalFunction* object = static_cast(instance); diff --git a/lib/Runtime/Library/JavascriptFunction.cpp b/lib/Runtime/Library/JavascriptFunction.cpp index 4f8e4f679e5..a924e16a34d 100644 --- a/lib/Runtime/Library/JavascriptFunction.cpp +++ b/lib/Runtime/Library/JavascriptFunction.cpp @@ -2504,12 +2504,6 @@ void __cdecl _alloca_probe_16() return PropertyQueryFlags::Property_Found; } break; - case PropertyIds::length: - if (this->IsScriptFunction()) - { - return PropertyQueryFlags::Property_Found; - } - break; } return DynamicObject::HasPropertyQuery(propertyId, info); } @@ -2607,12 +2601,6 @@ void __cdecl _alloca_probe_16() return false; } break; - case PropertyIds::length: - if (this->IsScriptFunction() || this->IsBoundFunction()) - { - return true; - } - break; } } return DynamicObject::IsConfigurable(propertyId); @@ -2631,12 +2619,6 @@ void __cdecl _alloca_probe_16() return false; } break; - case PropertyIds::length: - if (this->IsScriptFunction()) - { - return false; - } - break; } } return DynamicObject::IsEnumerable(propertyId); @@ -2655,12 +2637,6 @@ void __cdecl _alloca_probe_16() return false; } break; - case PropertyIds::length: - if (this->IsScriptFunction()) - { - return false; - } - break; } } return DynamicObject::IsWritable(propertyId); @@ -2676,18 +2652,6 @@ void __cdecl _alloca_probe_16() return true; } - if (index == length) - { - if (this->IsScriptFunction() || this->IsBoundFunction()) - { - if (DynamicObject::GetPropertyIndex(PropertyIds::length) == Constants::NoSlot) - { - //Only for user defined functions length is a special property. - *propertyName = requestContext->GetPropertyString(PropertyIds::length); - return true; - } - } - } return false; } @@ -2978,17 +2942,6 @@ void __cdecl _alloca_probe_16() return true; } - if (propertyId == PropertyIds::length) - { - FunctionProxy *proxy = this->GetFunctionProxy(); - if (proxy) - { - *value = TaggedInt::ToVarUnchecked(proxy->EnsureDeserialized()->GetReportedInParamsCount() - 1); - *result = true; - return true; - } - } - return false; } @@ -3010,14 +2963,6 @@ void __cdecl _alloca_probe_16() isReadOnly = true; } break; - - case PropertyIds::length: - if (this->IsScriptFunction()) - { - isReadOnly = true; - } - break; - } if (isReadOnly) @@ -3079,13 +3024,6 @@ void __cdecl _alloca_probe_16() return false; } break; - case PropertyIds::length: - if (this->IsScriptFunction()) - { - JavascriptError::ThrowCantDeleteIfStrictMode(flags, this->GetScriptContext(), this->GetScriptContext()->GetPropertyName(propertyId)->GetBuffer()); - return false; - } - break; } BOOL result = DynamicObject::DeleteProperty(propertyId, flags); @@ -3113,14 +3051,6 @@ void __cdecl _alloca_probe_16() return false; } } - else if (BuiltInPropertyRecords::length.Equals(propertyNameString)) - { - if (this->IsScriptFunction()) - { - JavascriptError::ThrowCantDeleteIfStrictMode(flags, this->GetScriptContext(), propertyNameString->GetString()); - return false; - } - } BOOL result = DynamicObject::DeleteProperty(propertyNameString, flags); diff --git a/lib/Runtime/Library/JavascriptLibrary.cpp b/lib/Runtime/Library/JavascriptLibrary.cpp index 2228f36fc8c..0a9a5ab62ef 100644 --- a/lib/Runtime/Library/JavascriptLibrary.cpp +++ b/lib/Runtime/Library/JavascriptLibrary.cpp @@ -44,6 +44,19 @@ namespace Js SimplePropertyDescriptor(NO_WRITE_BARRIER_TAG(BuiltInPropertyRecords::name), PropertyConfigurable) }; + SimplePropertyDescriptor const JavascriptLibrary::FunctionWithPrototypeLengthAndNameTypeDescriptors[3] = + { + SimplePropertyDescriptor(NO_WRITE_BARRIER_TAG(BuiltInPropertyRecords::prototype), PropertyWritable), + SimplePropertyDescriptor(NO_WRITE_BARRIER_TAG(BuiltInPropertyRecords::length), PropertyConfigurable), + SimplePropertyDescriptor(NO_WRITE_BARRIER_TAG(BuiltInPropertyRecords::name), PropertyConfigurable) + }; + + SimplePropertyDescriptor const JavascriptLibrary::FunctionWithPrototypeAndLengthTypeDescriptors[2] = + { + SimplePropertyDescriptor(NO_WRITE_BARRIER_TAG(BuiltInPropertyRecords::prototype), PropertyWritable), + SimplePropertyDescriptor(NO_WRITE_BARRIER_TAG(BuiltInPropertyRecords::length), PropertyConfigurable) + }; + SimplePropertyDescriptor const JavascriptLibrary::ModuleNamespaceTypeDescriptors[1] = { SimplePropertyDescriptor(NO_WRITE_BARRIER_TAG(BuiltInPropertyRecords::_symbolToStringTag), PropertyNone) @@ -57,6 +70,8 @@ namespace Js SimpleTypeHandler<1> JavascriptLibrary::SharedFunctionWithConfigurableLengthTypeHandler(NO_WRITE_BARRIER_TAG(BuiltInPropertyRecords::length), PropertyConfigurable); SimpleTypeHandler<1> JavascriptLibrary::SharedFunctionWithLengthTypeHandler(NO_WRITE_BARRIER_TAG(BuiltInPropertyRecords::length)); SimpleTypeHandler<2> JavascriptLibrary::SharedFunctionWithLengthAndNameTypeHandler(NO_WRITE_BARRIER_TAG(FunctionWithLengthAndNameTypeDescriptors)); + SimpleTypeHandler<3> JavascriptLibrary::SharedFunctionWithPrototypeLengthAndNameTypeHandler(NO_WRITE_BARRIER_TAG(FunctionWithPrototypeLengthAndNameTypeDescriptors)); + SimpleTypeHandler<2> JavascriptLibrary::SharedFunctionWithPrototypeAndLengthTypeHandler(NO_WRITE_BARRIER_TAG(FunctionWithPrototypeAndLengthTypeDescriptors)); SimpleTypeHandler<1> JavascriptLibrary::SharedNamespaceSymbolTypeHandler(NO_WRITE_BARRIER_TAG(ModuleNamespaceTypeDescriptors), PropertyTypesHasSpecialProperties); MissingPropertyTypeHandler JavascriptLibrary::MissingPropertyHolderTypeHandler; @@ -542,12 +557,14 @@ namespace Js PathTypeHandlerNoAttr::New(scriptContext, this->GetRootPath(), 0, 0, 0, true, true), true, true); variantDateType = StaticType::New(scriptContext, TypeIds_VariantDate, nullValue, nullptr); - anonymousFunctionTypeHandler = NullTypeHandler::GetDefaultInstance(); - anonymousFunctionWithPrototypeTypeHandler = &SharedFunctionWithPrototypeTypeHandlerV11; - // Initialize function types + anonymousFunctionTypeHandler = &SharedFunctionWithConfigurableLengthTypeHandler; + anonymousFunctionWithPrototypeTypeHandler = &SharedFunctionWithPrototypeAndLengthTypeHandler; + functionTypeHandler = &SharedFunctionWithoutPrototypeTypeHandler; + functionTypeHandlerWithLength = &SharedFunctionWithLengthAndNameTypeHandler; + functionWithPrototypeAndLengthTypeHandler = &SharedFunctionWithPrototypeLengthAndNameTypeHandler; functionWithPrototypeTypeHandler = &SharedFunctionWithPrototypeTypeHandler; functionWithPrototypeTypeHandler->SetHasKnownSlot0(); @@ -724,7 +741,7 @@ namespace Js JavascriptArray::EnsureCalculationOfAllocationBuckets(); } - template + template bool JavascriptLibrary::InitializeFunction(DynamicObject *instance, DeferredTypeHandlerBase * typeHandler, DeferredInitializeMode mode) { JavascriptFunction * function = JavascriptFunction::FromVar(instance); @@ -739,12 +756,25 @@ namespace Js if (!addPrototype) { - Assert(!useAnonymous); - typeHandler->ConvertFunction(function, javascriptLibrary->functionTypeHandler); + if (!useAnonymous && addName) + { + typeHandler->ConvertFunction(function, useLengthType ? javascriptLibrary->functionTypeHandlerWithLength : javascriptLibrary->functionTypeHandler); + } + else + { + typeHandler->ConvertFunction(function, javascriptLibrary->anonymousFunctionTypeHandler); + } } else { - typeHandler->ConvertFunction(function, useAnonymous ? javascriptLibrary->anonymousFunctionWithPrototypeTypeHandler : javascriptLibrary->functionWithPrototypeTypeHandler); + if (useAnonymous) + { + typeHandler->ConvertFunction(function, javascriptLibrary->anonymousFunctionWithPrototypeTypeHandler); + } + else + { + typeHandler->ConvertFunction(function, useLengthType ? javascriptLibrary->functionWithPrototypeAndLengthTypeHandler : javascriptLibrary->functionWithPrototypeTypeHandler); + } DynamicObject *protoObject = javascriptLibrary->CreateConstructorPrototypeObject(function); if (scriptFunction && scriptFunction->GetFunctionInfo()->IsClassConstructor()) { @@ -756,14 +786,28 @@ namespace Js } } - if(scriptFunction && (useAnonymous || scriptFunction->GetFunctionProxy()->EnsureDeserialized()->GetIsStaticNameFunction())) + if (scriptFunction) { - return true; + ParseableFunctionInfo * funcInfo = scriptFunction->GetFunctionProxy()->EnsureDeserialized(); + + if (useLengthType) + { + function->SetPropertyWithAttributes(PropertyIds::length, TaggedInt::ToVarUnchecked(funcInfo->GetReportedInParamsCount() - 1), PropertyConfigurable, nullptr, PropertyOperation_None, SideEffects_None); + } + + if (useAnonymous || funcInfo->GetIsStaticNameFunction()) + { + return true; + } } - JavascriptString * functionName = nullptr; - if (((Js::JavascriptFunction*)function)->GetFunctionName(&functionName)) + + if (addName) { - function->SetPropertyWithAttributes(PropertyIds::name, functionName, PropertyConfigurable, nullptr); + JavascriptString * functionName = nullptr; + if (((Js::JavascriptFunction*)function)->GetFunctionName(&functionName)) + { + function->SetPropertyWithAttributes(PropertyIds::name, functionName, PropertyConfigurable, nullptr); + } } return true; @@ -830,7 +874,7 @@ namespace Js template DynamicTypeHandler * JavascriptLibrary::GetDeferredFunctionTypeHandlerBase() { - return DeferredTypeHandler, InitializeFunctionDeferredTypeHandlerFilter>::GetDefaultInstance(); + return DeferredTypeHandler, InitializeFunctionDeferredTypeHandlerFilter>::GetDefaultInstance(); } template @@ -868,7 +912,7 @@ namespace Js DynamicTypeHandler * JavascriptLibrary::GetDeferredAnonymousPrototypeFunctionTypeHandler() { - return JavascriptLibrary::GetDeferredFunctionTypeHandlerBase(); + return JavascriptLibrary::GetDeferredFunctionTypeHandlerBase(); } DynamicTypeHandler * JavascriptLibrary::GetDeferredPrototypeFunctionTypeHandler(ScriptContext* scriptContext) @@ -876,6 +920,11 @@ namespace Js return JavascriptLibrary::GetDeferredFunctionTypeHandlerBase(); } + DynamicTypeHandler * JavascriptLibrary::GetDeferredPrototypeFunctionWithNameAndLengthTypeHandler() + { + return JavascriptLibrary::GetDeferredFunctionTypeHandlerBase(); + } + DynamicTypeHandler * JavascriptLibrary::GetDeferredPrototypeFunctionWithLengthTypeHandler(ScriptContext* scriptContext) { return DeferredTypeHandler>::GetDefaultInstance(); @@ -883,12 +932,17 @@ namespace Js DynamicTypeHandler * JavascriptLibrary::GetDeferredAnonymousFunctionTypeHandler() { - return anonymousFunctionTypeHandler; + return JavascriptLibrary::GetDeferredFunctionTypeHandlerBase(); } DynamicTypeHandler * JavascriptLibrary::GetDeferredFunctionTypeHandler() { - return GetDeferredFunctionTypeHandlerBase(); + return GetDeferredFunctionTypeHandlerBase(); + } + + DynamicTypeHandler * JavascriptLibrary::GetDeferredFunctionTypeHandlerNoPrototype() + { + return GetDeferredFunctionTypeHandlerBase(); } DynamicTypeHandler * JavascriptLibrary::ScriptFunctionTypeHandler(bool noPrototypeProperty, bool isAnonymousFunction) @@ -899,13 +953,13 @@ namespace Js { scriptFunctionTypeHandler = isAnonymousFunction ? this->GetDeferredAnonymousFunctionTypeHandler() : - this->GetDeferredFunctionTypeHandler(); + this->GetDeferredFunctionTypeHandlerNoPrototype(); } else { scriptFunctionTypeHandler = isAnonymousFunction ? JavascriptLibrary::GetDeferredAnonymousPrototypeFunctionTypeHandler() : - JavascriptLibrary::GetDeferredPrototypeFunctionTypeHandler(scriptContext); + JavascriptLibrary::GetDeferredPrototypeFunctionWithNameAndLengthTypeHandler(); } return scriptFunctionTypeHandler; } diff --git a/lib/Runtime/Library/JavascriptLibrary.h b/lib/Runtime/Library/JavascriptLibrary.h index 783a0411fef..041d5e9cad6 100644 --- a/lib/Runtime/Library/JavascriptLibrary.h +++ b/lib/Runtime/Library/JavascriptLibrary.h @@ -312,6 +312,8 @@ namespace Js Field(DynamicTypeHandler *) anonymousFunctionTypeHandler; Field(DynamicTypeHandler *) anonymousFunctionWithPrototypeTypeHandler; Field(DynamicTypeHandler *) functionTypeHandler; + Field(DynamicTypeHandler *) functionTypeHandlerWithLength; + Field(DynamicTypeHandler *) functionWithPrototypeAndLengthTypeHandler; Field(DynamicTypeHandler *) functionWithPrototypeTypeHandler; Field(DynamicType *) externalFunctionWithDeferredPrototypeType; Field(DynamicType *) externalFunctionWithLengthAndDeferredPrototypeType; @@ -513,6 +515,8 @@ namespace Js static SimpleTypeHandler<2> SharedFunctionWithLengthAndNameTypeHandler; static SimpleTypeHandler<2> SharedIdMappedFunctionWithPrototypeTypeHandler; static SimpleTypeHandler<1> SharedNamespaceSymbolTypeHandler; + static SimpleTypeHandler<3> SharedFunctionWithPrototypeLengthAndNameTypeHandler; + static SimpleTypeHandler<2> SharedFunctionWithPrototypeAndLengthTypeHandler; static MissingPropertyTypeHandler MissingPropertyHolderTypeHandler; static SimplePropertyDescriptor const SharedFunctionPropertyDescriptors[2]; @@ -520,6 +524,8 @@ namespace Js static SimplePropertyDescriptor const HeapArgumentsPropertyDescriptors[3]; static SimplePropertyDescriptor const FunctionWithLengthAndPrototypeTypeDescriptors[2]; static SimplePropertyDescriptor const FunctionWithLengthAndNameTypeDescriptors[2]; + static SimplePropertyDescriptor const FunctionWithPrototypeLengthAndNameTypeDescriptors[3]; + static SimplePropertyDescriptor const FunctionWithPrototypeAndLengthTypeDescriptors[2]; static SimplePropertyDescriptor const ModuleNamespaceTypeDescriptors[1]; public: @@ -917,6 +923,8 @@ namespace Js static DynamicTypeHandler * GetDeferredAnonymousPrototypeAsyncFunctionTypeHandler(); DynamicTypeHandler * GetDeferredFunctionTypeHandler(); + DynamicTypeHandler * GetDeferredFunctionTypeHandlerNoPrototype(); + DynamicTypeHandler * GetDeferredPrototypeFunctionWithNameAndLengthTypeHandler(); DynamicTypeHandler * ScriptFunctionTypeHandler(bool noPrototypeProperty, bool isAnonymousFunction); DynamicTypeHandler * GetDeferredAnonymousFunctionTypeHandler(); template @@ -1248,7 +1256,7 @@ namespace Js #endif public: - template + template static bool __cdecl InitializeFunction(DynamicObject* function, DeferredTypeHandlerBase * typeHandler, DeferredInitializeMode mode); virtual void Finalize(bool isShutdown) override; diff --git a/lib/Runtime/Types/SimpleTypeHandler.cpp b/lib/Runtime/Types/SimpleTypeHandler.cpp index 2e025e454fc..381f963faec 100644 --- a/lib/Runtime/Types/SimpleTypeHandler.cpp +++ b/lib/Runtime/Types/SimpleTypeHandler.cpp @@ -1239,6 +1239,7 @@ namespace Js template class SimpleTypeHandler<1>; template class SimpleTypeHandler<2>; + template class SimpleTypeHandler<3>; template class SimpleTypeHandler<6>; } diff --git a/test/Function/funcAndboundFuncLength.js b/test/Function/funcAndboundFuncLength.js new file mode 100644 index 00000000000..f10d72ac17d --- /dev/null +++ b/test/Function/funcAndboundFuncLength.js @@ -0,0 +1,150 @@ +//------------------------------------------------------------------------------------------------------- +// Copyright (C) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. +//------------------------------------------------------------------------------------------------------- + +WScript.LoadScriptFile("..\\UnitTestFramework\\UnitTestFramework.js"); + +function lengthDefaultState(func, paramCount, identifier) +{ + "use strict"; + const descriptor = Object.getOwnPropertyDescriptor(func, "length"); + + assert.isTrue(descriptor.configurable, identifier + " length is configurable"); + assert.isFalse(descriptor.writable, identifier + " length is not writeable"); + assert.isFalse(descriptor.enumerable, identifier + " length is not enumerable"); + assert.isTrue(func.hasOwnProperty("length"), identifier + " should have length property"); + assert.areEqual(func.length, paramCount, identifier + " length property should default to number of parameters"); + assert.throws(()=>{func.length = 5;}, TypeError, "Writing to " + identifier + " length should throw type error"); +} + +function deleteLength(func, identifier) +{ + "use strict"; + assert.doesNotThrow(()=>{delete func.length}, "Deleting " + identifier + ".length should not throw"); + assert.isTrue(!func.hasOwnProperty("length"), "Deleting " + identifier + ".length should succeed"); + assert.areEqual(0, func.length, identifier + ".length once deleted should return 0 i.e. prototype value"); + assert.throws(()=>{func.length = 5}, TypeError, "Attempting to write to " + identifier + " deleted length should throw in strict mode"); + assert.isTrue(!func.hasOwnProperty("length"), "recreating deleted " + identifier + ".length fails"); + assert.areEqual(0, func.length, identifier + ".length once deleted should return 0 i.e. prototype value"); + reDefineLengthStrict(func, identifier); +} + +function reDefineLengthStrict(func, identifier) +{ + "use strict"; + const initialValue = func.length; + assert.throws(()=>{func.length = initialValue - 1}, TypeError, "Writing to " + identifier + ".length throws in strict mode"); + assert.areEqual(initialValue, func.length, "Failed attempt to write to " + identifier + ".length does not change it's value"); + Object.defineProperty(func, "length", {enumerable : true, writable : true, value : initialValue + 1}); + const descriptor = Object.getOwnPropertyDescriptor(func, "length"); + + assert.isTrue(descriptor.writable, identifier + " after redefinition length can be writeable"); + assert.isTrue(descriptor.enumerable, identifier + " after redefinition length can be enumerable"); + assert.areEqual(initialValue + 1, func.length, identifier + ".length after redefinition takes new value"); + func.length = initialValue - 1; + assert.areEqual(initialValue - 1, func.length, identifier + ".length after redefinition can be writeable"); +} + +function reDefineLength(func, identifier) +{ + const initialValue = func.length; + assert.doesNotThrow(()=>{func.length = initialValue - 1}, "Writing to " + identifier + ".length does not throw when not in strict mode"); + assert.areEqual(initialValue, func.length, "Failed attempt to write to " + identifier + ".length does not change it's value"); + Object.defineProperty(func, "length", {enumerable : true, writable : true, value : initialValue + 1}); + const descriptor = Object.getOwnPropertyDescriptor(func, "length"); + + assert.isTrue(descriptor.writable, identifier + " after redefinition length can be writeable"); + assert.isTrue(descriptor.enumerable, identifier + " after redefinition length can be enumerable"); + assert.areEqual(initialValue + 1, func.length, identifier + ".length after redefinition takes new value"); + func.length = initialValue - 1; + assert.areEqual(initialValue - 1, func.length, identifier + ".length after redefinition can be writeable"); +} + + +const tests = [ + { + name : "function.length default state", + body : function() + { + function normalFunction (a, b) { } + const anonymousFunction = function (a, b, c) { } + const lambda = (a, b, c, d) => { } + lengthDefaultState(normalFunction, 2, "function"); + lengthDefaultState(anonymousFunction, 3, "Anonymous function"); + lengthDefaultState(lambda, 4, "Lambda function"); + deleteLength(normalFunction, "function"); + deleteLength(anonymousFunction, "Anonymous function"); + deleteLength(lambda, "Lambda function"); + } + }, + { + name : "Redefining function.length with defineProperty", + body : function() + { + function normalFunction (a, b) { } + const anonymousFunction = function (a, b, c) { } + const lambda = (a, b, c, d) => { } + reDefineLength(normalFunction, "function"); + reDefineLength(anonymousFunction, "Anonymous function"); + reDefineLength(lambda, "Lambda function"); + } + }, + { + name : "bound function.length default state", + body : function() + { + function normalFunction (a, b) { } + const anonymousFunction = function (a, b, c) { } + const lambda = (a, b, c, d) => { } + const boundNormal = normalFunction.bind({}, 1); + const boundAnon = anonymousFunction.bind({}, 1, 1, 1, 1); + const boundLambda = lambda.bind({}, 1, 1); + + lengthDefaultState(boundNormal, 1, "Bound Function"); + lengthDefaultState(boundAnon, 0, "Anonymous Bound Function"); + lengthDefaultState(boundLambda, 2, "Bound Lambda Function"); + deleteLength(boundNormal, "Bound Function"); + deleteLength(boundAnon, "Anonymous Bound Function"); + deleteLength(boundLambda, "Bound Lambda Function"); + } + }, + { + name : "Redefining boundFunction.length with defineProperty", + body : function() + { + function normalFunction (a, b) { } + const anonymousFunction = function (a, b, c) { } + const lambda = (a, b, c, d) => { } + const boundNormal = normalFunction.bind({}, 1); + const boundAnon = anonymousFunction.bind({}, 1, 1, 1, 1); + const boundLambda = lambda.bind({}, 1, 1); + reDefineLength(boundNormal, "Bound Function"); + reDefineLength(boundAnon, "Anonymous Bound Function"); + reDefineLength(boundLambda, "Bound Lambda Function"); + } + }, + { + name : "Lengths of different bound functions", + body : function() + { + const targ = function(a, b) {}; + let testBound = targ.bind({}); + assert.areEqual(testBound.length, 2, "Bound function uses target function's length when created"); + Object.defineProperty(targ, "length", {value : 5, writable : true}); + testBound = targ.bind({}); + assert.areEqual(testBound.length, 5, "Bound function uses target function's length when created if different to arg count"); + testBound = targ.bind({}, 1, 2); + assert.areEqual(testBound.length, 3, "Bound function deducts bound parameters from length when created"); + targ.length = 1; + assert.areEqual(testBound.length, 3, "Bound function length does not change if target function length is changed"); + testBound = targ.bind({}, 1, 2); + assert.areEqual(testBound.length, 0, "Bound function length will default to 0 if it would be negative"); + delete targ.length; + testBound = targ.bind({}); + assert.areEqual(testBound.length, 0, "Bound function length will default to 0 if target function has no length property"); + } + } +]; + +testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" }); diff --git a/test/Function/rlexe.xml b/test/Function/rlexe.xml index 7a36f19cb0c..e4bccc1d7f8 100644 --- a/test/Function/rlexe.xml +++ b/test/Function/rlexe.xml @@ -85,6 +85,12 @@ -args summary -endargs + + + funcAndboundFuncLength.js + -args summary -endargs + + call1.js diff --git a/test/es6/ES6Function_bugs.js b/test/es6/ES6Function_bugs.js index 36694f8b978..ff1f9821fd5 100644 --- a/test/es6/ES6Function_bugs.js +++ b/test/es6/ES6Function_bugs.js @@ -28,7 +28,7 @@ var tests = [ get: function () { } }); } - assert.doesNotThrow(function () { g('length') }, "assertion failure on defineProperty 'length' with getter after sealing a function object"); + assert.throws(function () { g('length') }, TypeError, "Cannot redefine non-configurable property 'length'"); assert.throws(function () { g('arguments') }, TypeError, "Cannot redefine non-configurable property 'arguments'"); assert.throws(function () { g('caller') }, TypeError, "Cannot redefine non-configurable property 'caller'"); }