Skip to content

Commit b153a8a

Browse files
committed
Data: Patch $._data as well
Also, extract the util to patch a prototype as we'll need it in the event module as well.
1 parent 0ea1015 commit b153a8a

File tree

3 files changed

+83
-61
lines changed

3 files changed

+83
-61
lines changed

Diff for: src/jquery/data.js

+18-59
Original file line numberDiff line numberDiff line change
@@ -1,86 +1,45 @@
1-
import { migratePatchFunc, migrateWarn } from "../main.js";
1+
import { migratePatchFunc } from "../main.js";
2+
import { patchProto } from "../utils.js";
23

34
function patchDataProto( original, options ) {
4-
var i,
5+
var warningId = options.warningId,
56
apiName = options.apiName,
6-
isInstanceMethod = options.isInstanceMethod,
7+
isInstanceMethod = options.isInstanceMethod;
78

8-
// `Object.prototype` keys are not enumerable so list the
9-
// official ones here. An alternative would be wrapping
10-
// data objects with a Proxy but that creates additional issues
11-
// like breaking object identity on subsequent calls.
12-
objProtoKeys = [
13-
"__proto__",
14-
"__defineGetter__",
15-
"__defineSetter__",
16-
"__lookupGetter__",
17-
"__lookupSetter__",
18-
"hasOwnProperty",
19-
"isPrototypeOf",
20-
"propertyIsEnumerable",
21-
"toLocaleString",
22-
"toString",
23-
"valueOf"
24-
],
25-
26-
// Use a null prototype at the beginning so that we can define our
27-
// `__proto__` getter & setter. We'll reset the prototype afterwards.
28-
intermediateDataObj = Object.create( null );
29-
30-
for ( i = 0; i < objProtoKeys.length; i++ ) {
31-
( function( key ) {
32-
Object.defineProperty( intermediateDataObj, key, {
33-
get: function() {
34-
migrateWarn( "data-null-proto",
35-
"Accessing properties from " + apiName +
36-
" inherited from Object.prototype is removed" );
37-
return ( key + "__cache" ) in intermediateDataObj ?
38-
intermediateDataObj[ key + "__cache" ] :
39-
Object.prototype[ key ];
40-
},
41-
set: function( value ) {
42-
migrateWarn( "data-null-proto",
43-
"Setting properties from " + apiName +
44-
" inherited from Object.prototype is removed" );
45-
intermediateDataObj[ key + "__cache" ] = value;
46-
}
47-
} );
48-
} )( objProtoKeys[ i ] );
49-
}
50-
51-
Object.setPrototypeOf( intermediateDataObj, Object.prototype );
52-
53-
return function jQueryDataProtoPatched() {
9+
return function apiWithProtoPatched() {
5410
var result = original.apply( this, arguments );
5511

5612
if ( arguments.length !== ( isInstanceMethod ? 0 : 1 ) || result === undefined ) {
5713
return result;
5814
}
5915

60-
// Insert an additional object in the prototype chain between `result`
61-
// and `Object.prototype`; that intermediate object proxies properties
62-
// to `Object.prototype`, warning about their usage first.
63-
Object.setPrototypeOf( result, intermediateDataObj );
16+
patchProto( result, {
17+
warningId: warningId,
18+
apiName: apiName
19+
} );
6420

6521
return result;
6622
};
6723
}
6824

69-
// Yes, we are patching jQuery.data twice; here & above. This is necessary
70-
// so that each of the two patches can be independently disabled.
7125
migratePatchFunc( jQuery, "data",
7226
patchDataProto( jQuery.data, {
27+
warningId: "data-null-proto",
7328
apiName: "jQuery.data()",
74-
isPrivateData: false,
29+
isInstanceMethod: false
30+
} ),
31+
"data-null-proto" );
32+
migratePatchFunc( jQuery, "_data",
33+
patchDataProto( jQuery._data, {
34+
warningId: "data-null-proto",
35+
apiName: "jQuery._data()",
7536
isInstanceMethod: false
7637
} ),
7738
"data-null-proto" );
7839
migratePatchFunc( jQuery.fn, "data",
7940
patchDataProto( jQuery.fn.data, {
41+
warningId: "data-null-proto",
8042
apiName: "jQuery.fn.data()",
81-
isPrivateData: true,
8243
isInstanceMethod: true
8344
} ),
8445
"data-null-proto" );
85-
86-
// TODO entry in warnings.md

Diff for: src/utils.js

+59
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,64 @@
1+
import { migrateWarn } from "./main.js";
2+
13
export function camelCase( string ) {
24
return string.replace( /-([a-z])/g, function( _, letter ) {
35
return letter.toUpperCase();
46
} );
57
}
8+
9+
// Make `object` inherit from `Object.prototype` via an additional object
10+
// in between; that intermediate object proxies properties
11+
// to `Object.prototype`, warning about their usage first.
12+
export function patchProto( object, options ) {
13+
var i,
14+
warningId = options.warningId,
15+
apiName = options.apiName,
16+
17+
// `Object.prototype` keys are not enumerable so list the
18+
// official ones here. An alternative would be wrapping
19+
// objects with a Proxy but that creates additional issues
20+
// like breaking object identity on subsequent calls.
21+
objProtoKeys = [
22+
"__proto__",
23+
"__defineGetter__",
24+
"__defineSetter__",
25+
"__lookupGetter__",
26+
"__lookupSetter__",
27+
"hasOwnProperty",
28+
"isPrototypeOf",
29+
"propertyIsEnumerable",
30+
"toLocaleString",
31+
"toString",
32+
"valueOf"
33+
],
34+
35+
// Use a null prototype at the beginning so that we can define our
36+
// `__proto__` getter & setter. We'll reset the prototype afterward.
37+
intermediateObj = Object.create( null );
38+
39+
for ( i = 0; i < objProtoKeys.length; i++ ) {
40+
( function( key ) {
41+
Object.defineProperty( intermediateObj, key, {
42+
get: function() {
43+
migrateWarn( warningId,
44+
"Accessing properties from " + apiName +
45+
" inherited from Object.prototype is removed" );
46+
return ( key + "__cache" ) in intermediateObj ?
47+
intermediateObj[ key + "__cache" ] :
48+
Object.prototype[ key ];
49+
},
50+
set: function( value ) {
51+
migrateWarn( warningId,
52+
"Setting properties from " + apiName +
53+
" inherited from Object.prototype is removed" );
54+
intermediateObj[ key + "__cache" ] = value;
55+
}
56+
} );
57+
} )( objProtoKeys[ i ] );
58+
}
59+
60+
Object.setPrototypeOf( intermediateObj, Object.prototype );
61+
Object.setPrototypeOf( object, intermediateObj );
62+
63+
return object;
64+
}

Diff for: test/unit/jquery/data.js

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,25 @@
11
QUnit.module( "data" );
22

33
QUnit.test( "properties from Object.prototype", function( assert ) {
4-
assert.expect( 6 );
4+
assert.expect( 8 );
55

66
var div = jQuery( "<div>" ).appendTo( "#qunit-fixture" );
77

88
div.data( "foo", "bar" );
9+
jQuery._data( div[ 0 ], "baz", "qaz" );
910

1011
expectNoMessage( assert, "Regular properties", function() {
1112
assert.strictEqual( div.data( "foo" ), "bar", "data access" );
1213
assert.strictEqual( jQuery.data( div[ 0 ], "foo" ), "bar", "data access (static method)" );
14+
assert.strictEqual( jQuery._data( div[ 0 ], "baz" ), "qaz", "private data access" );
1315
} );
1416

15-
expectMessage( assert, "Properties from Object.prototype", 2, function() {
17+
expectMessage( assert, "Properties from Object.prototype", 3, function() {
1618
assert.ok( div.data().hasOwnProperty( "foo" ),
1719
"hasOwnProperty works" );
1820
assert.ok( jQuery.data( div[ 0 ] ).hasOwnProperty( "foo" ),
1921
"hasOwnProperty works (static method)" );
22+
assert.ok( jQuery._data( div[ 0 ] ).hasOwnProperty( "baz" ),
23+
"hasOwnProperty works (private data)" );
2024
} );
2125
} );

0 commit comments

Comments
 (0)