Skip to content

Commit fd459a1

Browse files
committed
[MERGE #976 @leirocks] #274 Remove [[Enumerate]] and associated reflective capabilities
Merge pull request #976 from leirocks:proxyenumerator #274 Remove [[Enumerate]] and associated reflective capabilities Fixes #274
2 parents 04dc49a + 98557fd commit fd459a1

File tree

9 files changed

+176
-88
lines changed

9 files changed

+176
-88
lines changed

lib/Runtime/Library/JavascriptBuiltInFunctionList.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -945,7 +945,6 @@ BUILTIN(JavascriptPromise, AllResolveElementFunction, EntryAllResolveElementFunc
945945
BUILTIN(JavascriptPromise, GetterSymbolSpecies, EntryGetterSymbolSpecies, FunctionInfo::ErrorOnNew)
946946
BUILTIN(JavascriptReflect, DefineProperty, EntryDefineProperty, FunctionInfo::ErrorOnNew | FunctionInfo::DoNotProfile)
947947
BUILTIN(JavascriptReflect, DeleteProperty, EntryDeleteProperty, FunctionInfo::ErrorOnNew | FunctionInfo::DoNotProfile)
948-
BUILTIN(JavascriptReflect, Enumerate, EntryEnumerate, FunctionInfo::ErrorOnNew | FunctionInfo::DoNotProfile)
949948
BUILTIN(JavascriptReflect, Get, EntryGet, FunctionInfo::ErrorOnNew | FunctionInfo::DoNotProfile)
950949
BUILTIN(JavascriptReflect, GetOwnPropertyDescriptor, EntryGetOwnPropertyDescriptor, FunctionInfo::ErrorOnNew | FunctionInfo::DoNotProfile)
951950
BUILTIN(JavascriptReflect, GetPrototypeOf, EntryGetPrototypeOf, FunctionInfo::ErrorOnNew | FunctionInfo::DoNotProfile)

lib/Runtime/Library/JavascriptLibrary.cpp

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3059,8 +3059,6 @@ namespace Js
30593059
library->AddFunctionToLibraryObject(reflectObject, PropertyIds::defineProperty, &JavascriptReflect::EntryInfo::DefineProperty, 3));
30603060
scriptContext->SetBuiltInLibraryFunction(JavascriptReflect::EntryInfo::DeleteProperty.GetOriginalEntryPoint(),
30613061
library->AddFunctionToLibraryObject(reflectObject, PropertyIds::deleteProperty, &JavascriptReflect::EntryInfo::DeleteProperty, 2));
3062-
scriptContext->SetBuiltInLibraryFunction(JavascriptReflect::EntryInfo::Enumerate.GetOriginalEntryPoint(),
3063-
library->AddFunctionToLibraryObject(reflectObject, PropertyIds::enumerate, &JavascriptReflect::EntryInfo::Enumerate, 1));
30643062
scriptContext->SetBuiltInLibraryFunction(JavascriptReflect::EntryInfo::Get.GetOriginalEntryPoint(),
30653063
library->AddFunctionToLibraryObject(reflectObject, PropertyIds::get, &JavascriptReflect::EntryInfo::Get, 2));
30663064
scriptContext->SetBuiltInLibraryFunction(JavascriptReflect::EntryInfo::GetOwnPropertyDescriptor.GetOriginalEntryPoint(),
@@ -7338,7 +7336,6 @@ namespace Js
73387336

73397337
REG_OBJECTS_LIB_FUNC(defineProperty, JavascriptReflect::EntryDefineProperty);
73407338
REG_OBJECTS_LIB_FUNC(deleteProperty, JavascriptReflect::EntryDeleteProperty);
7341-
REG_OBJECTS_LIB_FUNC(enumerate, JavascriptReflect::EntryEnumerate);
73427339
REG_OBJECTS_LIB_FUNC(get, JavascriptReflect::EntryGet);
73437340
REG_OBJECTS_LIB_FUNC(getOwnPropertyDescriptor, JavascriptReflect::EntryGetOwnPropertyDescriptor);
73447341
REG_OBJECTS_LIB_FUNC(getPrototypeOf, JavascriptReflect::EntryGetPrototypeOf);

lib/Runtime/Library/JavascriptProxy.cpp

Lines changed: 45 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -894,39 +894,56 @@ namespace Js
894894
// the proxy has been revoked; TypeError.
895895
if (!threadContext->RecordImplicitException())
896896
return FALSE;
897-
JavascriptError::ThrowTypeError(GetScriptContext(), JSERR_ErrorOnRevokedProxy, _u("enumerate"));
897+
JavascriptError::ThrowTypeError(GetScriptContext(), JSERR_ErrorOnRevokedProxy, _u("ownKeys"));
898+
}
899+
900+
Var enmeratorObj;
901+
Var propertyName = nullptr;
902+
PropertyId propertyId;
903+
int index = 0;
904+
JsUtil::BaseDictionary<const char16*, Var, Recycler> dict(scriptContext->GetRecycler());
905+
JavascriptArray* arrResult = scriptContext->GetLibrary()->CreateArray();
906+
907+
// 13.7.5.15 EnumerateObjectProperties(O) (https://tc39.github.io/ecma262/#sec-enumerate-object-properties)
908+
// for (let key of Reflect.ownKeys(obj)) {
909+
Var trapResult = JavascriptOperators::GetOwnPropertyNames(this, scriptContext);
910+
if (JavascriptArray::Is(trapResult))
911+
{
912+
((JavascriptArray*)trapResult)->GetEnumerator(false, &enmeratorObj, scriptContext);
913+
JavascriptEnumerator* pEnumerator = JavascriptEnumerator::FromVar(enmeratorObj);
914+
while ((propertyName = pEnumerator->MoveAndGetNext(propertyId)) != NULL)
915+
{
916+
PropertyId propId = JavascriptOperators::GetPropertyId(propertyName, scriptContext);
917+
Var prop = JavascriptOperators::GetProperty(RecyclableObject::FromVar(trapResult), propId, scriptContext);
918+
// if (typeof key === "string") {
919+
if (JavascriptString::Is(prop))
920+
{
921+
Js::PropertyDescriptor desc;
922+
JavascriptString* str = JavascriptString::FromVar(prop);
923+
// let desc = Reflect.getOwnPropertyDescriptor(obj, key);
924+
BOOL ret = JavascriptOperators::GetOwnPropertyDescriptor(this, str, scriptContext, &desc);
925+
// if (desc && !visited.has(key)) {
926+
if (ret && !dict.ContainsKey(str->GetSz()))
927+
{
928+
dict.Add(str->GetSz(), prop);
929+
// if (desc.enumerable) yield key;
930+
if (desc.IsEnumerable())
931+
{
932+
ret = arrResult->SetItem(index++, prop, PropertyOperation_None);
933+
Assert(ret);
934+
}
935+
}
936+
}
937+
}
898938
}
899-
900-
//4. Let trap be the result of GetMethod(handler, "enumerate").
901-
//5. ReturnIfAbrupt(trap).
902-
//6. If trap is undefined, then
903-
//a.Return the result of calling the[[Enumerate]] internal method of target.
904-
//7. Let trapResult be the result of calling the[[Call]] internal method of trap with handler as the this value and a new List containing target.
905-
//8. ReturnIfAbrupt(trapResult).
906-
//9. If Type(trapResult) is not Object, then throw a TypeError exception.
907-
//10. Return trapResult.
908-
JavascriptFunction* getEnumeratorMethod = GetMethodHelper(PropertyIds::enumerate, scriptContext);
909-
Assert(!GetScriptContext()->IsHeapEnumInProgress());
910-
if (nullptr == getEnumeratorMethod)
939+
else
911940
{
912-
return target->GetEnumerator(enumNonEnumerable, enumerator, requestContext, preferSnapshotSemantics, enumSymbols);
941+
AssertMsg(false, "Expect GetOwnPropertyNames result to be array");
913942
}
914943

915-
CallInfo callInfo(CallFlags_Value, 2);
916-
Var varArgs[2];
917-
Js::Arguments arguments(callInfo, varArgs);
918-
varArgs[0] = handler;
919-
varArgs[1] = target;
920-
921-
Js::ImplicitCallFlags saveImplicitCallFlags = threadContext->GetImplicitCallFlags();
922-
Var trapResult = getEnumeratorMethod->CallFunction(arguments);
923-
threadContext->SetImplicitCallFlags((Js::ImplicitCallFlags)(saveImplicitCallFlags | ImplicitCall_Accessor));
944+
*enumerator = IteratorObjectEnumerator::Create(scriptContext,
945+
JavascriptOperators::GetIterator(RecyclableObject::FromVar(arrResult), scriptContext));
924946

925-
if (!JavascriptOperators::IsObject(trapResult))
926-
{
927-
JavascriptError::ThrowTypeError(scriptContext, JSERR_InconsistentTrapResult, _u("enumerate"));
928-
}
929-
*enumerator = IteratorObjectEnumerator::Create(scriptContext, trapResult);
930947
return TRUE;
931948
}
932949

lib/Runtime/Library/JavascriptProxy.h

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -178,14 +178,17 @@ namespace Js
178178
JavascriptConversion::ToPropertyKey(element, scriptContext, &propertyRecord);
179179
propertyId = propertyRecord->GetPropertyId();
180180

181-
if (propertyId != Constants::NoProperty)
181+
if (!targetToTrapResultMap.ContainsKey(propertyId))
182182
{
183-
targetToTrapResultMap.Add(propertyId, true);
184-
}
185-
186-
if (fn(propertyRecord))
187-
{
188-
trapResult->DirectSetItemAt(trapResultIndex++, element);
183+
if (propertyId != Constants::NoProperty)
184+
{
185+
targetToTrapResultMap.Add(propertyId, true);
186+
}
187+
188+
if (fn(propertyRecord))
189+
{
190+
trapResult->DirectSetItemAt(trapResultIndex++, element);
191+
}
189192
}
190193
}
191194
}

lib/Runtime/Library/JavascriptReflect.cpp

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -83,33 +83,6 @@ namespace Js
8383
return JavascriptOperators::OP_DeleteElementI(target, propertyKey, scriptContext);
8484
}
8585

86-
Var JavascriptReflect::EntryEnumerate(RecyclableObject* function, CallInfo callInfo, ...)
87-
{
88-
PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
89-
90-
ARGUMENTS(args, callInfo);
91-
ScriptContext* scriptContext = function->GetScriptContext();
92-
AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, _u("Reflect.enumerate"));
93-
94-
AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
95-
if (args.Info.Flags & CallFlags_New)
96-
{
97-
JavascriptError::ThrowTypeError(scriptContext, JSERR_ErrorOnNew, _u("Reflect.enumerate"));
98-
}
99-
100-
if (args.Info.Count < 2 || !JavascriptOperators::IsObject(args[1]))
101-
{
102-
JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedObject, _u("Reflect.enumerate"));
103-
}
104-
Var target = args[1];
105-
106-
Var iterator = nullptr;
107-
Recycler* recycler = scriptContext->GetRecycler();
108-
ForInObjectEnumeratorWrapper* forinEnumerator = RecyclerNew(recycler, Js::ForInObjectEnumeratorWrapper, RecyclableObject::FromVar(target), scriptContext);
109-
iterator = JavascriptEnumeratorIterator::Create(forinEnumerator, nullptr, scriptContext);
110-
return iterator;
111-
}
112-
11386
Var JavascriptReflect::EntryGet(RecyclableObject* function, CallInfo callInfo, ...)
11487
{
11588
PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);

lib/Runtime/Library/JavascriptReflect.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ namespace Js
3535

3636
static Var EntryDefineProperty(RecyclableObject* function, CallInfo callInfo, ...);
3737
static Var EntryDeleteProperty(RecyclableObject* function, CallInfo callInfo, ...);
38-
static Var EntryEnumerate(RecyclableObject* function, CallInfo callInfo, ...);
3938
static Var EntryGet(RecyclableObject* function, CallInfo callInfo, ...);
4039
static Var EntryGetOwnPropertyDescriptor(RecyclableObject* function, CallInfo callInfo, ...);
4140
static Var EntryGetPrototypeOf(RecyclableObject* function, CallInfo callInfo, ...);

test/es6/proxyenumbug.js

Lines changed: 0 additions & 20 deletions
This file was deleted.

test/es6/proxyenumremoval.js

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
//-------------------------------------------------------------------------------------------------------
2+
// Copyright (C) Microsoft. All rights reserved.
3+
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
4+
//-------------------------------------------------------------------------------------------------------
5+
6+
var passed = 1;
7+
passed &= typeof Reflect.enumerate === 'undefined';
8+
9+
var proxy = new Proxy({}, {
10+
enumerate: function() {
11+
passed = 0;
12+
}
13+
});
14+
for(var key in proxy);
15+
16+
var keys=""
17+
var proxy = new Proxy({x:1,y:2}, {});
18+
for(var key in proxy){ keys += key;}
19+
passed &= keys==="xy";
20+
21+
// check ownKeys
22+
var keys=""
23+
var proxy = new Proxy({"5":1}, {
24+
ownKeys: function() {
25+
return ['a', {y:2}, 5, 'b', Symbol.iterator];
26+
}
27+
});
28+
try{
29+
for(var key in proxy);
30+
passed = false;
31+
}
32+
catch(e){}
33+
34+
// check property descriptor
35+
var keys=""
36+
var proxy = new Proxy({b:1,a:2}, {
37+
ownKeys: function() {
38+
return ['a', {y:2}, 5, 'b', Symbol.iterator];
39+
}
40+
});
41+
try{
42+
for(var key in proxy);
43+
passed = false;
44+
}
45+
catch(e){}
46+
47+
var keys=""
48+
var proxy = new Proxy({b:1,a:2}, {
49+
ownKeys: function() {
50+
return new Proxy(['a', 'b'],{});
51+
}
52+
});
53+
for(var key in proxy){ keys += key;}
54+
passed &= keys==="ab";
55+
56+
var keys=""
57+
var proxy = new Proxy({c:1,d:2}, {
58+
ownKeys: function() {
59+
return new Proxy(['a', 'b'],{
60+
get(target, propKey, receiver){
61+
return Reflect.get(['c', 'd'], propKey, receiver);
62+
}
63+
});
64+
}
65+
});
66+
for(var key in proxy){ keys += key;}
67+
passed &= keys==="cd";
68+
69+
var keys=""
70+
var proxy = new Proxy({b:1,a:2}, {
71+
ownKeys: function() {
72+
return {x:1,y:2, '0':'a'};
73+
}
74+
});
75+
for(var key in proxy){ keys += key;}
76+
passed &= keys==="";
77+
78+
var keys=""
79+
var proxy = new Proxy({b:1,a:2}, {
80+
ownKeys: function() {
81+
return {x:1,y:2, '0':'a', length:2};
82+
}
83+
});
84+
for(var key in proxy){ keys += key;}
85+
passed &= keys==="a";
86+
87+
// check property descriptor trap
88+
var keys=""
89+
var already_non_enmerable = false;
90+
var getPrototypeOfCalled = 0;
91+
var proxy = new Proxy({}, {
92+
ownKeys: function() {
93+
return ['a','b','a']; // make first a non-enumerable, and second a enumerable, second a won't show up in for-in
94+
},
95+
getOwnPropertyDescriptor: function(target, key){
96+
var enumerable = true;
97+
if(key === "a" && !already_non_enmerable)
98+
{
99+
enumerable=false;
100+
already_non_enmerable = true;
101+
}
102+
return {
103+
configurable: true,
104+
enumerable: enumerable,
105+
value: 42,
106+
writable: true
107+
};
108+
},
109+
getPrototypeOf: function(){
110+
getPrototypeOfCalled++;
111+
return null;
112+
}
113+
});
114+
for(var key in proxy){ keys += key;}
115+
passed &= keys==="b";
116+
passed &= getPrototypeOfCalled===1;
117+
118+
if (passed) {
119+
WScript.Echo("PASS");
120+
}

test/es6/rlexe.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -707,7 +707,7 @@
707707
</test>
708708
<test>
709709
<default>
710-
<files>proxyenumbug.js</files>
710+
<files>proxyenumremoval.js</files>
711711
</default>
712712
</test>
713713
<test>

0 commit comments

Comments
 (0)