Skip to content
This repository was archived by the owner on Oct 20, 2023. It is now read-only.

Commit a5b71a3

Browse files
committed
Improved support for Object.defineProperty
Issue angelozerr#377
1 parent 0caeb7e commit a5b71a3

File tree

4 files changed

+57
-1
lines changed

4 files changed

+57
-1
lines changed

defs/ecma5.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
"!doc": "Creates a new object with the specified prototype object and properties."
3030
},
3131
"defineProperty": {
32-
"!type": "fn(obj: ?, prop: string, desc: ?)",
32+
"!type": "fn(obj: ?, prop: string, desc: ?) -> !custom:Object_defineProperty",
3333
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/defineProperty",
3434
"!doc": "Defines a new property directly on an object, or modifies an existing property on an object, and returns the object. If you want to see how to use the Object.defineProperty method with a binary-flags-like syntax, see this article."
3535
},

lib/def.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,26 @@
449449
return result;
450450
});
451451

452+
var PropSpec = infer.constraint("target", {
453+
addType: function(tp) {
454+
if (!(tp instanceof infer.Obj)) return;
455+
if (tp.hasProp("value"))
456+
tp.getProp("value").propagate(this.target);
457+
else if (tp.hasProp("get"))
458+
tp.getProp("get").propagate(new infer.IsCallee(infer.ANull, [], null, this.target));
459+
}
460+
});
461+
462+
infer.registerFunction("Object_defineProperty", function(_self, args, argNodes) {
463+
if (argNodes && argNodes.length >= 3 && argNodes[1].type == "Literal" &&
464+
typeof argNodes[1].value == "string") {
465+
var obj = args[0], connect = new infer.AVal;
466+
obj.propagate(new infer.PropHasSubset(argNodes[1].value, connect, argNodes[1]));
467+
args[2].propagate(new PropSpec(connect));
468+
}
469+
return infer.ANull;
470+
});
471+
452472
var IsBound = infer.constraint("self, args, target", {
453473
addType: function(tp) {
454474
if (!(tp instanceof infer.Fn)) return;

plugin/doc_comment.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,21 @@
4545
ObjectExpression: function(node) {
4646
for (var i = 0; i < node.properties.length; ++i)
4747
attachComments(node.properties[i].key);
48+
},
49+
CallExpression: function(node) {
50+
if (isDefinePropertyCall(node)) attachComments(node);
4851
}
4952
});
5053
}
5154

55+
function isDefinePropertyCall(node) {
56+
return node.callee.type == "MemberExpression" &&
57+
node.callee.object.name == "Object" &&
58+
node.callee.property.name == "defineProperty" &&
59+
node.arguments.length >= 3 &&
60+
typeof node.arguments[1].value == "string";
61+
}
62+
5263
function postInfer(ast, scope) {
5364
jsdocParseTypedefs(ast.sourceFile.text, scope);
5465

@@ -76,6 +87,15 @@
7687
interpretComments(prop, key.commentsBefore, scope,
7788
node.objType.getProp(key.name));
7889
}
90+
},
91+
CallExpression: function(node, scope) {
92+
if (node.commentsBefore && isDefinePropertyCall(node)) {
93+
var type = infer.expressionType({node: node.arguments[0], state: scope}).getType();
94+
if (type && type instanceof infer.Obj) {
95+
var prop = type.props[node.arguments[1].value];
96+
if (prop) interpretComments(node, node.commentsBefore, scope, prop);
97+
}
98+
}
7999
}
80100
}, infer.searchVisitor, scope);
81101
}
@@ -333,6 +353,7 @@
333353
} else if (node.type == "AssignmentExpression") {
334354
if (node.right.type == "FunctionExpression")
335355
fn = node.right.body.scope.fnType;
356+
} else if (node.type == "CallExpression") {
336357
} else { // An object property
337358
if (node.value.type == "FunctionExpression") fn = node.value.body.scope.fnType;
338359
}

test/cases/defineProperty.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
var o = {};
2+
3+
// Docstring for prop1
4+
Object.defineProperty(o, "prop1", {
5+
get: function() { return "hi"; }
6+
});
7+
8+
Object.defineProperty(o, "prop2", {
9+
value: 100
10+
});
11+
12+
o.prop1; //: string
13+
o.prop2; //: number
14+
15+
o.prop1; //doc: Docstring for prop1

0 commit comments

Comments
 (0)