Skip to content

Commit 7699222

Browse files
committed
[MERGE #5405 @rhuanjl] Fix configurability of function.length and boundFunction.length
Merge pull request #5405 from rhuanjl:boundFunction Fix: #4122 This PR addresses several issues with the length property of JavascriptFunctions and BoundFunctions. Length should be configurable (previously worked for JavascriptFunctions but not BoundFunctions) Deleting length should succeed (previously failed for both) Deleting length should not throw in strict mode (previously failed for both) value after deletion = 0 (previously failed for both) using defineProperty to redefine length should work (previously failed silently for both)
2 parents 1ef7ea8 + cbada71 commit 7699222

11 files changed

+255
-288
lines changed

lib/Runtime/Debug/DiagObjectModel.cpp

+1-7
Original file line numberDiff line numberDiff line change
@@ -2547,7 +2547,6 @@ namespace Js
25472547
// We need to special-case RegExp constructor here because it has some special properties (above) and some
25482548
// special enumerable properties which should all show up in the debugger.
25492549
JavascriptRegExpConstructor* regExp = scriptContext->GetLibrary()->GetRegExpConstructor();
2550-
Js::JavascriptFunction* jsFunction = Js::JavascriptFunction::FromVar(object);
25512550

25522551
if (regExp == object)
25532552
{
@@ -2563,11 +2562,6 @@ namespace Js
25632562
InsertItem(originalObject, object, propertyId, isConst, isUnscoped, &pMethodsGroupWalker);
25642563
}
25652564
}
2566-
else if ((jsFunction->IsScriptFunction() && !jsFunction->GetFunctionProxy()->IsJsBuiltInCode()) || jsFunction->IsBoundFunction())
2567-
{
2568-
// Adding special property length for the ScriptFunction, like it is done in JavascriptFunction::GetSpecialNonEnumerablePropertyName
2569-
InsertItem(originalObject, object, PropertyIds::length, true/*not editable*/, false /*isUnscoped*/, &pMethodsGroupWalker);
2570-
}
25712565
}
25722566
}
25732567

@@ -4216,4 +4210,4 @@ namespace Js
42164210
}
42174211
#endif
42184212
}
4219-
#endif
4213+
#endif

lib/Runtime/Library/BoundFunction.cpp

+13-178
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ namespace Js
2424
count(0),
2525
boundArgs(nullptr)
2626
{
27-
2827
DebugOnly(VerifyEntryPoint());
2928
AssertMsg(args.Info.Count > 0, "wrong number of args in BoundFunction");
3029

@@ -44,21 +43,19 @@ namespace Js
4443
}
4544
type->SetPrototype(proto);
4645
}
46+
47+
int len = 0;
4748
// 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.
48-
// 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")
49-
if (JavascriptProxy::Is(targetFunction))
49+
// additionally need to get the correct length value for the boundFunctions' length property
50+
if (JavascriptOperators::HasOwnProperty(targetFunction, PropertyIds::length, scriptContext, nullptr) == TRUE)
5051
{
51-
if (JavascriptOperators::HasOwnProperty(targetFunction, PropertyIds::length, scriptContext, nullptr) == TRUE)
52+
Var varLength;
53+
if (targetFunction->GetProperty(targetFunction, PropertyIds::length, &varLength, nullptr, scriptContext))
5254
{
53-
int len = 0;
54-
Var varLength;
55-
if (targetFunction->GetProperty(targetFunction, PropertyIds::length, &varLength, nullptr, scriptContext))
56-
{
57-
len = JavascriptConversion::ToInt32(varLength, scriptContext);
58-
}
55+
len = JavascriptConversion::ToInt32(varLength, scriptContext);
5956
}
60-
GetTypeHandler()->EnsureObjectReady(this);
6157
}
58+
GetTypeHandler()->EnsureObjectReady(this);
6259

6360
if (args.Info.Count > 1)
6461
{
@@ -84,27 +81,12 @@ namespace Js
8481
// If no "this" is passed, "undefined" is used
8582
boundThis = scriptContext->GetLibrary()->GetUndefined();
8683
}
87-
}
8884

89-
BoundFunction::BoundFunction(RecyclableObject* targetFunction, Var boundThis, Var* args, uint argsCount, DynamicType * type)
90-
: JavascriptFunction(type, &functionInfo),
91-
count(argsCount),
92-
boundArgs(nullptr)
93-
{
94-
DebugOnly(VerifyEntryPoint());
95-
96-
this->targetFunction = targetFunction;
97-
this->boundThis = boundThis;
98-
99-
if (argsCount != 0)
100-
{
101-
this->boundArgs = RecyclerNewArray(this->GetScriptContext()->GetRecycler(), Field(Var), argsCount);
102-
103-
for (uint i = 0; i < argsCount; i++)
104-
{
105-
this->boundArgs[i] = args[i];
106-
}
107-
}
85+
// Reduce length number of bound args
86+
len = len - this->count;
87+
len = max(len, 0);
88+
89+
SetPropertyWithAttributes(PropertyIds::length, TaggedInt::ToVarUnchecked(len), PropertyConfigurable, nullptr, PropertyOperation_None, SideEffects_None);
10890
}
10991

11092
BoundFunction* BoundFunction::New(ScriptContext* scriptContext, ArgumentReader args)
@@ -307,108 +289,11 @@ namespace Js
307289
return false;
308290
}
309291

310-
PropertyQueryFlags BoundFunction::HasPropertyQuery(PropertyId propertyId, _Inout_opt_ PropertyValueInfo* info)
311-
{
312-
if (propertyId == PropertyIds::length)
313-
{
314-
return PropertyQueryFlags::Property_Found;
315-
}
316-
317-
return JavascriptFunction::HasPropertyQuery(propertyId, info);
318-
}
319-
320-
PropertyQueryFlags BoundFunction::GetPropertyQuery(Var originalInstance, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext)
321-
{
322-
BOOL result;
323-
if (GetPropertyBuiltIns(originalInstance, propertyId, value, info, requestContext, &result))
324-
{
325-
return JavascriptConversion::BooleanToPropertyQueryFlags(result);
326-
}
327-
328-
return JavascriptFunction::GetPropertyQuery(originalInstance, propertyId, value, info, requestContext);
329-
}
330-
331-
PropertyQueryFlags BoundFunction::GetPropertyQuery(Var originalInstance, JavascriptString* propertyNameString, Var* value, PropertyValueInfo* info, ScriptContext* requestContext)
332-
{
333-
BOOL result;
334-
PropertyRecord const* propertyRecord;
335-
this->GetScriptContext()->FindPropertyRecord(propertyNameString, &propertyRecord);
336-
337-
if (propertyRecord != nullptr && GetPropertyBuiltIns(originalInstance, propertyRecord->GetPropertyId(), value, info, requestContext, &result))
338-
{
339-
return JavascriptConversion::BooleanToPropertyQueryFlags(result);
340-
}
341-
342-
return JavascriptFunction::GetPropertyQuery(originalInstance, propertyNameString, value, info, requestContext);
343-
}
344-
345-
bool BoundFunction::GetPropertyBuiltIns(Var originalInstance, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext, BOOL* result)
346-
{
347-
if (propertyId == PropertyIds::length)
348-
{
349-
// Get the "length" property of the underlying target function
350-
int len = 0;
351-
Var varLength;
352-
if (targetFunction->GetProperty(targetFunction, PropertyIds::length, &varLength, nullptr, requestContext))
353-
{
354-
len = JavascriptConversion::ToInt32(varLength, requestContext);
355-
}
356-
357-
// Reduce by number of bound args
358-
len = len - this->count;
359-
len = max(len, 0);
360-
361-
*value = JavascriptNumber::ToVar(len, requestContext);
362-
*result = true;
363-
return true;
364-
}
365-
366-
return false;
367-
}
368-
369292
PropertyQueryFlags BoundFunction::GetPropertyReferenceQuery(Var originalInstance, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext)
370293
{
371294
return BoundFunction::GetPropertyQuery(originalInstance, propertyId, value, info, requestContext);
372295
}
373296

374-
BOOL BoundFunction::SetProperty(PropertyId propertyId, Var value, PropertyOperationFlags flags, PropertyValueInfo* info)
375-
{
376-
BOOL result;
377-
if (SetPropertyBuiltIns(propertyId, value, flags, info, &result))
378-
{
379-
return result;
380-
}
381-
382-
return JavascriptFunction::SetProperty(propertyId, value, flags, info);
383-
}
384-
385-
BOOL BoundFunction::SetProperty(JavascriptString* propertyNameString, Var value, PropertyOperationFlags flags, PropertyValueInfo* info)
386-
{
387-
BOOL result;
388-
PropertyRecord const* propertyRecord;
389-
this->GetScriptContext()->FindPropertyRecord(propertyNameString, &propertyRecord);
390-
391-
if (propertyRecord != nullptr && SetPropertyBuiltIns(propertyRecord->GetPropertyId(), value, flags, info, &result))
392-
{
393-
return result;
394-
}
395-
396-
return JavascriptFunction::SetProperty(propertyNameString, value, flags, info);
397-
}
398-
399-
bool BoundFunction::SetPropertyBuiltIns(PropertyId propertyId, Var value, PropertyOperationFlags flags, PropertyValueInfo* info, BOOL* result)
400-
{
401-
if (propertyId == PropertyIds::length)
402-
{
403-
JavascriptError::ThrowCantAssignIfStrictMode(flags, this->GetScriptContext());
404-
405-
*result = false;
406-
return true;
407-
}
408-
409-
return false;
410-
}
411-
412297
_Check_return_ _Success_(return) BOOL BoundFunction::GetAccessors(PropertyId propertyId, _Outptr_result_maybenull_ Var* getter, _Outptr_result_maybenull_ Var* setter, ScriptContext* requestContext)
413298
{
414299
return DynamicObject::GetAccessors(propertyId, getter, setter, requestContext);
@@ -429,56 +314,6 @@ namespace Js
429314
return SetProperty(propertyId, value, PropertyOperation_None, info);
430315
}
431316

432-
BOOL BoundFunction::DeleteProperty(PropertyId propertyId, PropertyOperationFlags flags)
433-
{
434-
if (propertyId == PropertyIds::length)
435-
{
436-
return false;
437-
}
438-
439-
return JavascriptFunction::DeleteProperty(propertyId, flags);
440-
}
441-
442-
BOOL BoundFunction::DeleteProperty(JavascriptString *propertyNameString, PropertyOperationFlags flags)
443-
{
444-
if (BuiltInPropertyRecords::length.Equals(propertyNameString))
445-
{
446-
return false;
447-
}
448-
449-
return JavascriptFunction::DeleteProperty(propertyNameString, flags);
450-
}
451-
452-
BOOL BoundFunction::IsWritable(PropertyId propertyId)
453-
{
454-
if (propertyId == PropertyIds::length)
455-
{
456-
return false;
457-
}
458-
459-
return JavascriptFunction::IsWritable(propertyId);
460-
}
461-
462-
BOOL BoundFunction::IsConfigurable(PropertyId propertyId)
463-
{
464-
if (propertyId == PropertyIds::length)
465-
{
466-
return false;
467-
}
468-
469-
return JavascriptFunction::IsConfigurable(propertyId);
470-
}
471-
472-
BOOL BoundFunction::IsEnumerable(PropertyId propertyId)
473-
{
474-
if (propertyId == PropertyIds::length)
475-
{
476-
return false;
477-
}
478-
479-
return JavascriptFunction::IsEnumerable(propertyId);
480-
}
481-
482317
BOOL BoundFunction::HasInstance(Var instance, ScriptContext* scriptContext, IsInstInlineCache* inlineCache)
483318
{
484319
return this->targetFunction->HasInstance(instance, scriptContext, inlineCache);

lib/Runtime/Library/BoundFunction.h

+1-12
Original file line numberDiff line numberDiff line change
@@ -19,31 +19,20 @@ namespace Js
1919
protected:
2020
BoundFunction(DynamicType * type);
2121
BoundFunction(Arguments args, DynamicType * type);
22-
BoundFunction(RecyclableObject* targetFunction, Var boundThis, Var* args, uint argsCount, DynamicType *type);
22+
2323
public:
2424
static BoundFunction* New(ScriptContext* scriptContext, ArgumentReader args);
2525

2626
static bool Is(Var func){ return JavascriptFunction::Is(func) && JavascriptFunction::UnsafeFromVar(func)->IsBoundFunction(); }
2727
static Var NewInstance(RecyclableObject* function, CallInfo callInfo, ...);
2828
virtual JavascriptString* GetDisplayNameImpl() const override;
29-
virtual PropertyQueryFlags HasPropertyQuery(PropertyId propertyId, _Inout_opt_ PropertyValueInfo* info) override;
30-
virtual PropertyQueryFlags GetPropertyQuery(Var originalInstance, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext) override;
31-
virtual PropertyQueryFlags GetPropertyQuery(Var originalInstance, JavascriptString* propertyNameString, Var* value, PropertyValueInfo* info, ScriptContext* requestContext) override;
3229
virtual PropertyQueryFlags GetPropertyReferenceQuery(Var originalInstance, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext) override;
33-
virtual BOOL SetProperty(PropertyId propertyId, Var value, PropertyOperationFlags flags, PropertyValueInfo* info) override;
34-
virtual BOOL SetProperty(JavascriptString* propertyNameString, Var value, PropertyOperationFlags flags, PropertyValueInfo* info) override;
3530

3631
_Check_return_ _Success_(return) virtual BOOL GetAccessors(PropertyId propertyId, _Outptr_result_maybenull_ Var* getter, _Outptr_result_maybenull_ Var* setter, ScriptContext* requestContext) override;
3732
virtual DescriptorFlags GetSetter(PropertyId propertyId, Var *setterValue, PropertyValueInfo* info, ScriptContext* requestContext) override;
3833
virtual DescriptorFlags GetSetter(JavascriptString* propertyNameString, Var *setterValue, PropertyValueInfo* info, ScriptContext* requestContext) override;
3934

4035
virtual BOOL InitProperty(PropertyId propertyId, Var value, PropertyOperationFlags flags = PropertyOperation_None, PropertyValueInfo* info = NULL) override;
41-
virtual BOOL DeleteProperty(PropertyId propertyId, PropertyOperationFlags flags) override;
42-
virtual BOOL DeleteProperty(JavascriptString *propertyNameString, PropertyOperationFlags flags) override;
43-
44-
virtual BOOL IsWritable(PropertyId propertyId) override;
45-
virtual BOOL IsConfigurable(PropertyId propertyId) override;
46-
virtual BOOL IsEnumerable(PropertyId propertyId) override;
4736
virtual BOOL HasInstance(Var instance, ScriptContext* scriptContext, IsInstInlineCache* inlineCache = NULL) override;
4837
virtual inline BOOL IsConstructor() const override;
4938

lib/Runtime/Library/JavascriptExternalFunction.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ namespace Js
5858

5959
bool __cdecl JavascriptExternalFunction::DeferredLengthInitializer(DynamicObject * instance, DeferredTypeHandlerBase * typeHandler, DeferredInitializeMode mode)
6060
{
61-
Js::JavascriptLibrary::InitializeFunction<true>(instance, typeHandler, mode);
61+
Js::JavascriptLibrary::InitializeFunction<true, true, true>(instance, typeHandler, mode);
6262

6363
JavascriptExternalFunction* object = static_cast<JavascriptExternalFunction*>(instance);
6464

0 commit comments

Comments
 (0)