-
Notifications
You must be signed in to change notification settings - Fork 29.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[querystring] avoid Object.prototype conflicts including __proto__
#5650
Conversation
ci: https://ci.nodejs.org/job/node-test-pull-request/1896/ a few nits.
|
Yeah ... you know ... I'd love to provide a test like var qsTestCases = [
['__proto__=1',
'__proto__=1',
{'__proto__': '1'}],
... etc etc ...
it doesn't, I was writing $ make -j8 test
make -C out BUILDTYPE=Release V=1
make[1]: Entering directory '/home/webreflection/code/node/out'
make[1]: Nothing to be done for 'all'.
make[1]: Leaving directory '/home/webreflection/code/node/out'
ln -fs out/Release/node node
Running main() from gtest_main.cc
[==========] Running 1 test from 1 test case.
[----------] Global test environment set-up.
[----------] 1 test from UtilTest
[ RUN ] UtilTest.ListHead
[ OK ] UtilTest.ListHead (0 ms)
[----------] 1 test from UtilTest (0 ms total)
[----------] Global test environment tear-down
[==========] 1 test from 1 test case ran. (0 ms total)
[ PASSED ] 1 test.
/usr/bin/python tools/test.py --mode=release message parallel sequential -J
=== release test-querystring ===
Path: parallel/test-querystring
assert.js:89
throw new assert.AssertionError({
^
AssertionError: {} deepEqual { __proto__: '1' }
at /home/webreflection/code/node/test/parallel/test-querystring.js:99:10
at Array.forEach (native)
at Object.<anonymous> (/home/webreflection/code/node/test/parallel/test-querystring.js:98:13)
at Module._compile (module.js:417:34)
at Object.Module._extensions..js (module.js:426:10)
at Module.load (module.js:357:32)
at Function.Module._load (module.js:314:12)
at Function.Module.runMain (module.js:451:10)
at startup (node.js:151:18)
at node.js:965:3
Command: out/Release/node /home/webreflection/code/node/test/parallel/test-querystring.js
[01:06|% 100|+ 1046|- 1]: Done
Makefile:115: recipe for target 'test' failed
make: *** [test] Error 1
However, with my changes in, everything is just fine as you can see in my console after building with the PR you've nit-picked ;-) $ ./out/Release/node
> require('querystring')
{ unescapeBuffer: [Function],
unescape: [Function: qsUnescape],
escape: [Function],
encode: [Function],
stringify: [Function],
decode: [Function],
parse: [Function] }
> require('querystring').parse('a=1')
{ a: '1' }
> require('querystring').parse('__proto__=1')
{ __proto__: '1' }
> Object.getOwnPropertyNames(require('querystring').parse('__proto__=1'))
[ '__proto__' ]
> Now try to execute any of those commands in current stable node or latest from master and see different results. As Summary: what should I do about tests? I kinda feel stuck in a loop I should PR fixes for Please let me know how to proceed. |
I would like to see benchmark results before and after this change. |
Also, I wouldn't say |
Me too but here's the catch: querystring is broken, we need to fix it. It's not an option to not fix it because maybe it got slightly slower. I hope you agree, otherwise there won't ever be interoperability with query strings all over the web, where standards didn't accept edge cases like |
@WebReflection I'm not arguing about the need for a fix. I'm more interested in finding the best performing solution to the problem. |
Despite what I think, what should be the way to test that in your opinion ? I can use |
My apoligies, I've realized you're right, assert.deepEqueal is not responsible, my test is. Will push a better one right now and see if my assumption about swallowing objects was right. |
Doing something like this should be fine if nothing else: const result = querystring.parse('__proto__=1');
const keys = Object.keys(result);
assert.deepEqual(keys, ['__proto__']);
assert.strictEqual(result[keys[0]], '1'); |
I've done hopefully a better test, I've used Like I've said, it's half past midnight here so I'm probably better off coding now. I'll try to quickly change the commit message if I manage, I don't know how to bench best, common, and worst cases against previous version though, any hint would be appreciated. Best Regards |
Running the
Also make sure that you copy the binary from You may need to do that a few times to get a decent feel for how the changes affected performance as the results can vary between runs depending on the overall system load and other factors. |
These are two runs of the bench. The version in ~/tmp/node/Release/node is the version built with this patch, the version in out/Release/node is the current file in master without the patch. To be extra clear, the version in $ ~/tmp/node/Release/node
> require('querystring').parse('__proto__')
{ __proto__: '' }
> while the one in $ out/Release/node
> require('querystring').parse('__proto__')
{}
> Following the result of the bench, it's kinda expected, like I've commented in the code. $ node benchmark/compare.js -r -g ~/tmp/node/Release/node out/Release/node -- querystring querystring-parse
running /home/webreflection/tmp/node/Release/node
querystring/querystring-parse.js
running out/Release/node
querystring/querystring-parse.js
querystring/querystring-parse.js type=noencode n=1000000: /home/webreflection/tmp/node/Release/node: 583690 out/Release/node: 610220 ........ -4.35%
querystring/querystring-parse.js type=multicharsep n=1000000: /home/webreflection/tmp/node/Release/node: 471900 out/Release/node: 544890 ... -13.40%
querystring/querystring-parse.js type=encodemany n=1000000: /home/webreflection/tmp/node/Release/node: 350630 out/Release/node: 356800 ...... -1.73%
querystring/querystring-parse.js type=encodelast n=1000000: /home/webreflection/tmp/node/Release/node: 515560 out/Release/node: 554390 ...... -7.00%
querystring/querystring-parse.js type=multivalue n=1000000: /home/webreflection/tmp/node/Release/node: 462950 out/Release/node: 525150 ..... -11.84%
querystring/querystring-parse.js type=multivaluemany n=1000000: /home/webreflection/tmp/node/Release/node: 197960 out/Release/node: 239460 . -17.33%
querystring/querystring-parse.js type=manypairs n=1000000: /home/webreflection/tmp/node/Release/node: 144000 out/Release/node: 126990 ....... 13.39%
[webreflection@archibold node]$ node benchmark/compare.js -r -g ~/tmp/node/Release/node out/Release/node -- querystring querystring-parse
running /home/webreflection/tmp/node/Release/node
querystring/querystring-parse.js
running out/Release/node
querystring/querystring-parse.js
querystring/querystring-parse.js type=noencode n=1000000: /home/webreflection/tmp/node/Release/node: 479990 out/Release/node: 620210 ....... -22.61%
querystring/querystring-parse.js type=multicharsep n=1000000: /home/webreflection/tmp/node/Release/node: 533420 out/Release/node: 568270 .... -6.13%
querystring/querystring-parse.js type=encodemany n=1000000: /home/webreflection/tmp/node/Release/node: 350320 out/Release/node: 363190 ...... -3.54%
querystring/querystring-parse.js type=encodelast n=1000000: /home/webreflection/tmp/node/Release/node: 504480 out/Release/node: 556740 ...... -9.39%
querystring/querystring-parse.js type=multivalue n=1000000: /home/webreflection/tmp/node/Release/node: 475120 out/Release/node: 531250 ..... -10.57%
querystring/querystring-parse.js type=multivaluemany n=1000000: /home/webreflection/tmp/node/Release/node: 193320 out/Release/node: 239250 . -19.20%
querystring/querystring-parse.js type=manypairs n=1000000: /home/webreflection/tmp/node/Release/node: 145510 out/Release/node: 140270 ........ 3.73% Is there anything else I should do? |
Weird...I got this benchmark on one out of 5 runs:
The others showed similar results to @WebReflection's |
Maybe something else was happening during the test and it got slower ...cause it makes no much sense otherwise, the |
In order to solve #5642 , keeping an eye and actually most likely improving performance for #728, this PR is meant to show a better way to deal with inheritance. As most common CS related problem say, "hashes" are faster than O(N*?) searches in an array and this is the reason I've proposed a solution based on `in` operator first.
not sure I've messed up with the flow here, I've tried to clean up the commit WebReflection@b121816 If you want a fresh new PR let me know so I can reset this one and make a better (single) one. |
This PR is fine, the commits can be squashed and cleaned up on landing if necessary. |
damn it, bad timing. I've pushed a freesh new clean PR #5722 and dropped the previous fork for this one. |
;-) no worries |
In order to solve #5642 , keeping an eye and actually most likely improving performance for #728 , this PR is meant to show a better way to deal with inheritance.
Mostly all problems would be solved using
const obj = Object.create(null);
instead ofconst obj = {};
on top, but we all know that's going to break around more code than it will solve.This PR is one option, the alternative is to grab, each time, at runtime,
Object.getOwnPropertyNames(Object.prototype)
and use doubleindexOf
per each key, one for the known keys in object, one for the known keys in its prototype in case keys in object aren't known.As most common CS related problem say, "hashes" are faster than
O(N*?)
searches in an array and this is the reason I've proposed a solution based onin
operator first, but we know JS hases aren't even close to be comparable to structs or heap operations.