spoint 0.1.0 → 0.1.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +134 -209
- package/SKILL.md +95 -0
- package/apps/environment/index.js +200 -1
- package/apps/environment/models/decorative/.gitkeep +0 -0
- package/apps/environment/models/hazards/.gitkeep +0 -0
- package/apps/environment/models/interactive/.gitkeep +0 -0
- package/apps/environment/models/structures/.gitkeep +0 -0
- package/apps/environment/smartObjects.js +114 -0
- package/apps/interactable/index.js +155 -0
- package/apps/physics-crate/index.js +15 -9
- package/apps/power-crate/index.js +18 -12
- package/apps/tps-game/$GDUPI.vrm +0 -0
- package/apps/tps-game/Cleetus.vrm +0 -0
- package/apps/tps-game/index.js +185 -27
- package/apps/world/index.js +68 -22
- package/bin/create-app.js +337 -0
- package/client/ARControls.js +301 -0
- package/client/LoadingManager.js +117 -0
- package/client/MobileControls.js +1122 -0
- package/client/anim-lib.glb +0 -0
- package/client/animation.js +306 -0
- package/client/app.js +1341 -65
- package/client/camera.js +191 -33
- package/client/createLoadingScreen.js +69 -0
- package/client/editor/bridge.js +113 -0
- package/client/editor/css/main.css +794 -0
- package/client/editor/images/rotate.svg +4 -0
- package/client/editor/images/scale.svg +4 -0
- package/client/editor/images/translate.svg +4 -0
- package/client/editor/index.html +103 -0
- package/client/editor/js/Command.js +41 -0
- package/client/editor/js/Config.js +81 -0
- package/client/editor/js/Editor.js +785 -0
- package/client/editor/js/EditorControls.js +438 -0
- package/client/editor/js/History.js +321 -0
- package/client/editor/js/Loader.js +987 -0
- package/client/editor/js/LoaderUtils.js +90 -0
- package/client/editor/js/Menubar.Add.js +510 -0
- package/client/editor/js/Menubar.Edit.js +145 -0
- package/client/editor/js/Menubar.File.js +466 -0
- package/client/editor/js/Menubar.Help.js +73 -0
- package/client/editor/js/Menubar.Status.js +51 -0
- package/client/editor/js/Menubar.View.js +183 -0
- package/client/editor/js/Menubar.js +27 -0
- package/client/editor/js/Player.js +53 -0
- package/client/editor/js/Resizer.js +58 -0
- package/client/editor/js/Script.js +503 -0
- package/client/editor/js/Selector.js +102 -0
- package/client/editor/js/Sidebar.Geometry.BoxGeometry.js +121 -0
- package/client/editor/js/Sidebar.Geometry.BufferGeometry.js +115 -0
- package/client/editor/js/Sidebar.Geometry.CapsuleGeometry.js +97 -0
- package/client/editor/js/Sidebar.Geometry.CircleGeometry.js +97 -0
- package/client/editor/js/Sidebar.Geometry.CylinderGeometry.js +121 -0
- package/client/editor/js/Sidebar.Geometry.DodecahedronGeometry.js +73 -0
- package/client/editor/js/Sidebar.Geometry.ExtrudeGeometry.js +196 -0
- package/client/editor/js/Sidebar.Geometry.IcosahedronGeometry.js +73 -0
- package/client/editor/js/Sidebar.Geometry.LatheGeometry.js +98 -0
- package/client/editor/js/Sidebar.Geometry.Modifiers.js +73 -0
- package/client/editor/js/Sidebar.Geometry.OctahedronGeometry.js +74 -0
- package/client/editor/js/Sidebar.Geometry.PlaneGeometry.js +97 -0
- package/client/editor/js/Sidebar.Geometry.RingGeometry.js +121 -0
- package/client/editor/js/Sidebar.Geometry.ShapeGeometry.js +76 -0
- package/client/editor/js/Sidebar.Geometry.SphereGeometry.js +133 -0
- package/client/editor/js/Sidebar.Geometry.TetrahedronGeometry.js +74 -0
- package/client/editor/js/Sidebar.Geometry.TorusGeometry.js +109 -0
- package/client/editor/js/Sidebar.Geometry.TorusKnotGeometry.js +121 -0
- package/client/editor/js/Sidebar.Geometry.TubeGeometry.js +135 -0
- package/client/editor/js/Sidebar.Geometry.js +332 -0
- package/client/editor/js/Sidebar.Material.BooleanProperty.js +60 -0
- package/client/editor/js/Sidebar.Material.ColorProperty.js +87 -0
- package/client/editor/js/Sidebar.Material.ConstantProperty.js +62 -0
- package/client/editor/js/Sidebar.Material.MapProperty.js +249 -0
- package/client/editor/js/Sidebar.Material.NumberProperty.js +60 -0
- package/client/editor/js/Sidebar.Material.Program.js +73 -0
- package/client/editor/js/Sidebar.Material.RangeValueProperty.js +63 -0
- package/client/editor/js/Sidebar.Material.js +751 -0
- package/client/editor/js/Sidebar.Object.Animation.js +102 -0
- package/client/editor/js/Sidebar.Object.js +898 -0
- package/client/editor/js/Sidebar.Project.App.js +165 -0
- package/client/editor/js/Sidebar.Project.Image.js +225 -0
- package/client/editor/js/Sidebar.Project.Materials.js +82 -0
- package/client/editor/js/Sidebar.Project.Renderer.js +144 -0
- package/client/editor/js/Sidebar.Project.Video.js +242 -0
- package/client/editor/js/Sidebar.Project.js +31 -0
- package/client/editor/js/Sidebar.Properties.js +73 -0
- package/client/editor/js/Sidebar.Scene.js +585 -0
- package/client/editor/js/Sidebar.Script.js +129 -0
- package/client/editor/js/Sidebar.Settings.History.js +146 -0
- package/client/editor/js/Sidebar.Settings.Shortcuts.js +175 -0
- package/client/editor/js/Sidebar.Settings.js +60 -0
- package/client/editor/js/Sidebar.js +41 -0
- package/client/editor/js/Storage.js +98 -0
- package/client/editor/js/Strings.js +2028 -0
- package/client/editor/js/Toolbar.js +84 -0
- package/client/editor/js/Viewport.Controls.js +92 -0
- package/client/editor/js/Viewport.Info.js +136 -0
- package/client/editor/js/Viewport.Pathtracer.js +91 -0
- package/client/editor/js/Viewport.ViewHelper.js +39 -0
- package/client/editor/js/Viewport.XR.js +222 -0
- package/client/editor/js/Viewport.js +900 -0
- package/client/editor/js/commands/AddObjectCommand.js +68 -0
- package/client/editor/js/commands/AddScriptCommand.js +75 -0
- package/client/editor/js/commands/Commands.js +23 -0
- package/client/editor/js/commands/MoveObjectCommand.js +111 -0
- package/client/editor/js/commands/MultiCmdsCommand.js +85 -0
- package/client/editor/js/commands/RemoveObjectCommand.js +88 -0
- package/client/editor/js/commands/RemoveScriptCommand.js +81 -0
- package/client/editor/js/commands/SetColorCommand.js +73 -0
- package/client/editor/js/commands/SetGeometryCommand.js +87 -0
- package/client/editor/js/commands/SetGeometryValueCommand.js +70 -0
- package/client/editor/js/commands/SetMaterialColorCommand.js +86 -0
- package/client/editor/js/commands/SetMaterialCommand.js +79 -0
- package/client/editor/js/commands/SetMaterialMapCommand.js +143 -0
- package/client/editor/js/commands/SetMaterialRangeCommand.js +91 -0
- package/client/editor/js/commands/SetMaterialValueCommand.js +90 -0
- package/client/editor/js/commands/SetMaterialVectorCommand.js +79 -0
- package/client/editor/js/commands/SetPositionCommand.js +84 -0
- package/client/editor/js/commands/SetRotationCommand.js +84 -0
- package/client/editor/js/commands/SetScaleCommand.js +84 -0
- package/client/editor/js/commands/SetSceneCommand.js +103 -0
- package/client/editor/js/commands/SetScriptValueCommand.js +80 -0
- package/client/editor/js/commands/SetShadowValueCommand.js +73 -0
- package/client/editor/js/commands/SetUuidCommand.js +70 -0
- package/client/editor/js/commands/SetValueCommand.js +75 -0
- package/client/editor/js/libs/acorn/acorn.js +3236 -0
- package/client/editor/js/libs/acorn/acorn_loose.js +1299 -0
- package/client/editor/js/libs/acorn/walk.js +344 -0
- package/client/editor/js/libs/app/index.html +57 -0
- package/client/editor/js/libs/app.js +251 -0
- package/client/editor/js/libs/codemirror/addon/dialog.css +32 -0
- package/client/editor/js/libs/codemirror/addon/dialog.js +163 -0
- package/client/editor/js/libs/codemirror/addon/show-hint.css +36 -0
- package/client/editor/js/libs/codemirror/addon/show-hint.js +529 -0
- package/client/editor/js/libs/codemirror/addon/tern.css +87 -0
- package/client/editor/js/libs/codemirror/addon/tern.js +750 -0
- package/client/editor/js/libs/codemirror/codemirror.css +344 -0
- package/client/editor/js/libs/codemirror/codemirror.js +9849 -0
- package/client/editor/js/libs/codemirror/mode/glsl.js +233 -0
- package/client/editor/js/libs/codemirror/mode/javascript.js +959 -0
- package/client/editor/js/libs/codemirror/theme/monokai.css +41 -0
- package/client/editor/js/libs/esprima.js +6401 -0
- package/client/editor/js/libs/jsonlint.js +453 -0
- package/client/editor/js/libs/signals.min.js +14 -0
- package/client/editor/js/libs/tern-threejs/threejs.js +5031 -0
- package/client/editor/js/libs/ternjs/comment.js +87 -0
- package/client/editor/js/libs/ternjs/def.js +588 -0
- package/client/editor/js/libs/ternjs/doc_comment.js +401 -0
- package/client/editor/js/libs/ternjs/infer.js +1635 -0
- package/client/editor/js/libs/ternjs/polyfill.js +80 -0
- package/client/editor/js/libs/ternjs/signal.js +26 -0
- package/client/editor/js/libs/ternjs/tern.js +993 -0
- package/client/editor/js/libs/ui.js +1346 -0
- package/client/editor/js/libs/ui.three.js +855 -0
- package/client/facial-animation.js +455 -0
- package/client/index.html +7 -4
- package/client/loading.css +147 -0
- package/client/loading.html +25 -0
- package/client/style.css +251 -0
- package/package.json +7 -3
- package/server.js +9 -1
- package/src/apps/AppContext.js +1 -1
- package/src/apps/AppLoader.js +50 -37
- package/src/apps/AppRuntime.js +32 -8
- package/src/client/InputHandler.js +233 -0
- package/src/client/JitterBuffer.js +207 -0
- package/src/client/KalmanFilter.js +125 -0
- package/src/client/MessageHandler.js +101 -0
- package/src/client/PhysicsNetworkClient.js +141 -68
- package/src/client/ReconnectManager.js +62 -0
- package/src/client/SmoothInterpolation.js +127 -0
- package/src/client/SnapshotProcessor.js +144 -0
- package/src/connection/ConnectionManager.js +21 -3
- package/src/connection/SessionStore.js +13 -3
- package/src/index.client.js +4 -6
- package/src/netcode/EventLog.js +29 -15
- package/src/netcode/LagCompensator.js +25 -26
- package/src/netcode/NetworkState.js +4 -1
- package/src/netcode/PhysicsIntegration.js +20 -6
- package/src/netcode/PlayerManager.js +10 -2
- package/src/netcode/SnapshotEncoder.js +66 -19
- package/src/netcode/TickSystem.js +13 -4
- package/src/physics/World.js +66 -13
- package/src/protocol/msgpack.js +90 -63
- package/src/sdk/ReloadHandlers.js +12 -2
- package/src/sdk/ReloadManager.js +5 -0
- package/src/sdk/ServerHandlers.js +50 -11
- package/src/sdk/StaticHandler.js +22 -6
- package/src/sdk/TickHandler.js +101 -34
- package/src/sdk/scaffold.js +31 -0
- package/src/sdk/server.js +59 -33
- package/src/shared/movement.js +2 -1
- package/src/spatial/Octree.js +5 -0
- package/apps/interactive-door/index.js +0 -33
- package/apps/patrol-npc/index.js +0 -37
- package/src/connection/QualityMonitor.js +0 -46
- package/src/debug/StateInspector.js +0 -42
- package/src/index.js +0 -1
- package/src/index.server.js +0 -27
- package/src/protocol/Codec.js +0 -60
- package/src/protocol/SequenceTracker.js +0 -71
- package/src/sdk/ClientMessageHandler.js +0 -80
- package/src/sdk/client.js +0 -122
- package/world/kaira.glb +0 -0
- package/world/schwust.glb +0 -0
|
@@ -0,0 +1,1635 @@
|
|
|
1
|
+
// Main type inference engine
|
|
2
|
+
|
|
3
|
+
// Walks an AST, building up a graph of abstract values and constraints
|
|
4
|
+
// that cause types to flow from one node to another. Also defines a
|
|
5
|
+
// number of utilities for accessing ASTs and scopes.
|
|
6
|
+
|
|
7
|
+
// Analysis is done in a context, which is tracked by the dynamically
|
|
8
|
+
// bound cx variable. Use withContext to set the current context.
|
|
9
|
+
|
|
10
|
+
// For memory-saving reasons, individual types export an interface
|
|
11
|
+
// similar to abstract values (which can hold multiple types), and can
|
|
12
|
+
// thus be used in place abstract values that only ever contain a
|
|
13
|
+
// single type.
|
|
14
|
+
|
|
15
|
+
(function(root, mod) {
|
|
16
|
+
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
|
17
|
+
return mod(exports, require("acorn"), require("acorn/dist/acorn_loose"), require("acorn/dist/walk"),
|
|
18
|
+
require("./def"), require("./signal"));
|
|
19
|
+
if (typeof define == "function" && define.amd) // AMD
|
|
20
|
+
return define(["exports", "acorn/dist/acorn", "acorn/dist/acorn_loose", "acorn/dist/walk", "./def", "./signal"], mod);
|
|
21
|
+
mod(root.tern || (root.tern = {}), acorn, acorn, acorn.walk, tern.def, tern.signal); // Plain browser env
|
|
22
|
+
})(this, function(exports, acorn, acorn_loose, walk, def, signal) {
|
|
23
|
+
"use strict";
|
|
24
|
+
|
|
25
|
+
var toString = exports.toString = function(type, maxDepth, parent) {
|
|
26
|
+
if (!type || type == parent || maxDepth && maxDepth < -3) return "?";
|
|
27
|
+
return type.toString(maxDepth, parent);
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// A variant of AVal used for unknown, dead-end values. Also serves
|
|
31
|
+
// as prototype for AVals, Types, and Constraints because it
|
|
32
|
+
// implements 'empty' versions of all the methods that the code
|
|
33
|
+
// expects.
|
|
34
|
+
var ANull = exports.ANull = signal.mixin({
|
|
35
|
+
addType: function() {},
|
|
36
|
+
propagate: function() {},
|
|
37
|
+
getProp: function() { return ANull; },
|
|
38
|
+
forAllProps: function() {},
|
|
39
|
+
hasType: function() { return false; },
|
|
40
|
+
isEmpty: function() { return true; },
|
|
41
|
+
getFunctionType: function() {},
|
|
42
|
+
getObjType: function() {},
|
|
43
|
+
getType: function() {},
|
|
44
|
+
gatherProperties: function() {},
|
|
45
|
+
propagatesTo: function() {},
|
|
46
|
+
typeHint: function() {},
|
|
47
|
+
propHint: function() {},
|
|
48
|
+
toString: function() { return "?"; }
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
function extend(proto, props) {
|
|
52
|
+
var obj = Object.create(proto);
|
|
53
|
+
if (props) for (var prop in props) obj[prop] = props[prop];
|
|
54
|
+
return obj;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// ABSTRACT VALUES
|
|
58
|
+
|
|
59
|
+
var WG_DEFAULT = 100, WG_NEW_INSTANCE = 90, WG_MADEUP_PROTO = 10, WG_MULTI_MEMBER = 5,
|
|
60
|
+
WG_CATCH_ERROR = 5, WG_GLOBAL_THIS = 90, WG_SPECULATIVE_THIS = 2;
|
|
61
|
+
|
|
62
|
+
var AVal = exports.AVal = function() {
|
|
63
|
+
this.types = [];
|
|
64
|
+
this.forward = null;
|
|
65
|
+
this.maxWeight = 0;
|
|
66
|
+
};
|
|
67
|
+
AVal.prototype = extend(ANull, {
|
|
68
|
+
addType: function(type, weight) {
|
|
69
|
+
weight = weight || WG_DEFAULT;
|
|
70
|
+
if (this.maxWeight < weight) {
|
|
71
|
+
this.maxWeight = weight;
|
|
72
|
+
if (this.types.length == 1 && this.types[0] == type) return;
|
|
73
|
+
this.types.length = 0;
|
|
74
|
+
} else if (this.maxWeight > weight || this.types.indexOf(type) > -1) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
this.signal("addType", type);
|
|
79
|
+
this.types.push(type);
|
|
80
|
+
var forward = this.forward;
|
|
81
|
+
if (forward) withWorklist(function(add) {
|
|
82
|
+
for (var i = 0; i < forward.length; ++i) add(type, forward[i], weight);
|
|
83
|
+
});
|
|
84
|
+
},
|
|
85
|
+
|
|
86
|
+
propagate: function(target, weight) {
|
|
87
|
+
if (target == ANull || (target instanceof Type && this.forward && this.forward.length > 2)) return;
|
|
88
|
+
if (weight && weight != WG_DEFAULT) target = new Muffle(target, weight);
|
|
89
|
+
(this.forward || (this.forward = [])).push(target);
|
|
90
|
+
var types = this.types;
|
|
91
|
+
if (types.length) withWorklist(function(add) {
|
|
92
|
+
for (var i = 0; i < types.length; ++i) add(types[i], target, weight);
|
|
93
|
+
});
|
|
94
|
+
},
|
|
95
|
+
|
|
96
|
+
getProp: function(prop) {
|
|
97
|
+
if (prop == "__proto__" || prop == "✖") return ANull;
|
|
98
|
+
var found = (this.props || (this.props = Object.create(null)))[prop];
|
|
99
|
+
if (!found) {
|
|
100
|
+
found = this.props[prop] = new AVal;
|
|
101
|
+
this.propagate(new PropIsSubset(prop, found));
|
|
102
|
+
}
|
|
103
|
+
return found;
|
|
104
|
+
},
|
|
105
|
+
|
|
106
|
+
forAllProps: function(c) {
|
|
107
|
+
this.propagate(new ForAllProps(c));
|
|
108
|
+
},
|
|
109
|
+
|
|
110
|
+
hasType: function(type) {
|
|
111
|
+
return this.types.indexOf(type) > -1;
|
|
112
|
+
},
|
|
113
|
+
isEmpty: function() { return this.types.length === 0; },
|
|
114
|
+
getFunctionType: function() {
|
|
115
|
+
for (var i = this.types.length - 1; i >= 0; --i)
|
|
116
|
+
if (this.types[i] instanceof Fn) return this.types[i];
|
|
117
|
+
},
|
|
118
|
+
getObjType: function() {
|
|
119
|
+
var seen = null;
|
|
120
|
+
for (var i = this.types.length - 1; i >= 0; --i) {
|
|
121
|
+
var type = this.types[i];
|
|
122
|
+
if (!(type instanceof Obj)) continue;
|
|
123
|
+
if (type.name) return type;
|
|
124
|
+
if (!seen) seen = type;
|
|
125
|
+
}
|
|
126
|
+
return seen;
|
|
127
|
+
},
|
|
128
|
+
|
|
129
|
+
getType: function(guess) {
|
|
130
|
+
if (this.types.length === 0 && guess !== false) return this.makeupType();
|
|
131
|
+
if (this.types.length === 1) return this.types[0];
|
|
132
|
+
return canonicalType(this.types);
|
|
133
|
+
},
|
|
134
|
+
|
|
135
|
+
toString: function(maxDepth, parent) {
|
|
136
|
+
if (this.types.length == 0) return toString(this.makeupType(), maxDepth, parent);
|
|
137
|
+
if (this.types.length == 1) return toString(this.types[0], maxDepth, parent);
|
|
138
|
+
var simplified = simplifyTypes(this.types);
|
|
139
|
+
if (simplified.length > 2) return "?";
|
|
140
|
+
return simplified.map(function(tp) { return toString(tp, maxDepth, parent); }).join("|");
|
|
141
|
+
},
|
|
142
|
+
|
|
143
|
+
computedPropType: function() {
|
|
144
|
+
if (!this.propertyOf) return null;
|
|
145
|
+
if (this.propertyOf.hasProp("<i>")) {
|
|
146
|
+
var computedProp = this.propertyOf.getProp("<i>");
|
|
147
|
+
if (computedProp == this) return null;
|
|
148
|
+
return computedProp.getType();
|
|
149
|
+
} else if (this.propertyOf.maybeProps && this.propertyOf.maybeProps["<i>"] == this) {
|
|
150
|
+
for (var prop in this.propertyOf.props) {
|
|
151
|
+
var val = this.propertyOf.props[prop];
|
|
152
|
+
if (!val.isEmpty()) return val;
|
|
153
|
+
}
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
|
|
158
|
+
makeupType: function() {
|
|
159
|
+
var computed = this.computedPropType();
|
|
160
|
+
if (computed) return computed;
|
|
161
|
+
|
|
162
|
+
if (!this.forward) return null;
|
|
163
|
+
for (var i = this.forward.length - 1; i >= 0; --i) {
|
|
164
|
+
var hint = this.forward[i].typeHint();
|
|
165
|
+
if (hint && !hint.isEmpty()) {guessing = true; return hint;}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
var props = Object.create(null), foundProp = null;
|
|
169
|
+
for (var i = 0; i < this.forward.length; ++i) {
|
|
170
|
+
var prop = this.forward[i].propHint();
|
|
171
|
+
if (prop && prop != "length" && prop != "<i>" && prop != "✖" && prop != cx.completingProperty) {
|
|
172
|
+
props[prop] = true;
|
|
173
|
+
foundProp = prop;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
if (!foundProp) return null;
|
|
177
|
+
|
|
178
|
+
var objs = objsWithProp(foundProp);
|
|
179
|
+
if (objs) {
|
|
180
|
+
var matches = [];
|
|
181
|
+
search: for (var i = 0; i < objs.length; ++i) {
|
|
182
|
+
var obj = objs[i];
|
|
183
|
+
for (var prop in props) if (!obj.hasProp(prop)) continue search;
|
|
184
|
+
if (obj.hasCtor) obj = getInstance(obj);
|
|
185
|
+
matches.push(obj);
|
|
186
|
+
}
|
|
187
|
+
var canon = canonicalType(matches);
|
|
188
|
+
if (canon) {guessing = true; return canon;}
|
|
189
|
+
}
|
|
190
|
+
},
|
|
191
|
+
|
|
192
|
+
typeHint: function() { return this.types.length ? this.getType() : null; },
|
|
193
|
+
propagatesTo: function() { return this; },
|
|
194
|
+
|
|
195
|
+
gatherProperties: function(f, depth) {
|
|
196
|
+
for (var i = 0; i < this.types.length; ++i)
|
|
197
|
+
this.types[i].gatherProperties(f, depth);
|
|
198
|
+
},
|
|
199
|
+
|
|
200
|
+
guessProperties: function(f) {
|
|
201
|
+
if (this.forward) for (var i = 0; i < this.forward.length; ++i) {
|
|
202
|
+
var prop = this.forward[i].propHint();
|
|
203
|
+
if (prop) f(prop, null, 0);
|
|
204
|
+
}
|
|
205
|
+
var guessed = this.makeupType();
|
|
206
|
+
if (guessed) guessed.gatherProperties(f);
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
function similarAVal(a, b, depth) {
|
|
211
|
+
var typeA = a.getType(false), typeB = b.getType(false);
|
|
212
|
+
if (!typeA || !typeB) return true;
|
|
213
|
+
return similarType(typeA, typeB, depth);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function similarType(a, b, depth) {
|
|
217
|
+
if (!a || depth >= 5) return b;
|
|
218
|
+
if (a == b) return a;
|
|
219
|
+
if (!b) return a;
|
|
220
|
+
if (a.constructor != b.constructor) return false;
|
|
221
|
+
if (a.constructor == Arr) {
|
|
222
|
+
var innerA = a.getProp("<i>").getType(false);
|
|
223
|
+
if (!innerA) return b;
|
|
224
|
+
var innerB = b.getProp("<i>").getType(false);
|
|
225
|
+
if (!innerB || similarType(innerA, innerB, depth + 1)) return b;
|
|
226
|
+
} else if (a.constructor == Obj) {
|
|
227
|
+
var propsA = 0, propsB = 0, same = 0;
|
|
228
|
+
for (var prop in a.props) {
|
|
229
|
+
propsA++;
|
|
230
|
+
if (prop in b.props && similarAVal(a.props[prop], b.props[prop], depth + 1))
|
|
231
|
+
same++;
|
|
232
|
+
}
|
|
233
|
+
for (var prop in b.props) propsB++;
|
|
234
|
+
if (propsA && propsB && same < Math.max(propsA, propsB) / 2) return false;
|
|
235
|
+
return propsA > propsB ? a : b;
|
|
236
|
+
} else if (a.constructor == Fn) {
|
|
237
|
+
if (a.args.length != b.args.length ||
|
|
238
|
+
!a.args.every(function(tp, i) { return similarAVal(tp, b.args[i], depth + 1); }) ||
|
|
239
|
+
!similarAVal(a.retval, b.retval, depth + 1) || !similarAVal(a.self, b.self, depth + 1))
|
|
240
|
+
return false;
|
|
241
|
+
return a;
|
|
242
|
+
} else {
|
|
243
|
+
return false;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
var simplifyTypes = exports.simplifyTypes = function(types) {
|
|
248
|
+
var found = [];
|
|
249
|
+
outer: for (var i = 0; i < types.length; ++i) {
|
|
250
|
+
var tp = types[i];
|
|
251
|
+
for (var j = 0; j < found.length; j++) {
|
|
252
|
+
var similar = similarType(tp, found[j], 0);
|
|
253
|
+
if (similar) {
|
|
254
|
+
found[j] = similar;
|
|
255
|
+
continue outer;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
found.push(tp);
|
|
259
|
+
}
|
|
260
|
+
return found;
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
function canonicalType(types) {
|
|
264
|
+
var arrays = 0, fns = 0, objs = 0, prim = null;
|
|
265
|
+
for (var i = 0; i < types.length; ++i) {
|
|
266
|
+
var tp = types[i];
|
|
267
|
+
if (tp instanceof Arr) ++arrays;
|
|
268
|
+
else if (tp instanceof Fn) ++fns;
|
|
269
|
+
else if (tp instanceof Obj) ++objs;
|
|
270
|
+
else if (tp instanceof Prim) {
|
|
271
|
+
if (prim && tp.name != prim.name) return null;
|
|
272
|
+
prim = tp;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
var kinds = (arrays && 1) + (fns && 1) + (objs && 1) + (prim && 1);
|
|
276
|
+
if (kinds > 1) return null;
|
|
277
|
+
if (prim) return prim;
|
|
278
|
+
|
|
279
|
+
var maxScore = 0, maxTp = null;
|
|
280
|
+
for (var i = 0; i < types.length; ++i) {
|
|
281
|
+
var tp = types[i], score = 0;
|
|
282
|
+
if (arrays) {
|
|
283
|
+
score = tp.getProp("<i>").isEmpty() ? 1 : 2;
|
|
284
|
+
} else if (fns) {
|
|
285
|
+
score = 1;
|
|
286
|
+
for (var j = 0; j < tp.args.length; ++j) if (!tp.args[j].isEmpty()) ++score;
|
|
287
|
+
if (!tp.retval.isEmpty()) ++score;
|
|
288
|
+
} else if (objs) {
|
|
289
|
+
score = tp.name ? 100 : 2;
|
|
290
|
+
}
|
|
291
|
+
if (score >= maxScore) { maxScore = score; maxTp = tp; }
|
|
292
|
+
}
|
|
293
|
+
return maxTp;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// PROPAGATION STRATEGIES
|
|
297
|
+
|
|
298
|
+
function Constraint() {}
|
|
299
|
+
Constraint.prototype = extend(ANull, {
|
|
300
|
+
init: function() { this.origin = cx.curOrigin; }
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
var constraint = exports.constraint = function(props, methods) {
|
|
304
|
+
var body = "this.init();";
|
|
305
|
+
props = props ? props.split(", ") : [];
|
|
306
|
+
for (var i = 0; i < props.length; ++i)
|
|
307
|
+
body += "this." + props[i] + " = " + props[i] + ";";
|
|
308
|
+
var ctor = Function.apply(null, props.concat([body]));
|
|
309
|
+
ctor.prototype = Object.create(Constraint.prototype);
|
|
310
|
+
for (var m in methods) if (methods.hasOwnProperty(m)) ctor.prototype[m] = methods[m];
|
|
311
|
+
return ctor;
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
var PropIsSubset = constraint("prop, target", {
|
|
315
|
+
addType: function(type, weight) {
|
|
316
|
+
if (type.getProp)
|
|
317
|
+
type.getProp(this.prop).propagate(this.target, weight);
|
|
318
|
+
},
|
|
319
|
+
propHint: function() { return this.prop; },
|
|
320
|
+
propagatesTo: function() {
|
|
321
|
+
if (this.prop == "<i>" || !/[^\w_]/.test(this.prop))
|
|
322
|
+
return {target: this.target, pathExt: "." + this.prop};
|
|
323
|
+
}
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
var PropHasSubset = exports.PropHasSubset = constraint("prop, type, originNode", {
|
|
327
|
+
addType: function(type, weight) {
|
|
328
|
+
if (!(type instanceof Obj)) return;
|
|
329
|
+
var prop = type.defProp(this.prop, this.originNode);
|
|
330
|
+
if (!prop.origin) prop.origin = this.origin;
|
|
331
|
+
this.type.propagate(prop, weight);
|
|
332
|
+
},
|
|
333
|
+
propHint: function() { return this.prop; }
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
var ForAllProps = constraint("c", {
|
|
337
|
+
addType: function(type) {
|
|
338
|
+
if (!(type instanceof Obj)) return;
|
|
339
|
+
type.forAllProps(this.c);
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
function withDisabledComputing(fn, body) {
|
|
344
|
+
cx.disabledComputing = {fn: fn, prev: cx.disabledComputing};
|
|
345
|
+
try {
|
|
346
|
+
return body();
|
|
347
|
+
} finally {
|
|
348
|
+
cx.disabledComputing = cx.disabledComputing.prev;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
var IsCallee = exports.IsCallee = constraint("self, args, argNodes, retval", {
|
|
352
|
+
init: function() {
|
|
353
|
+
Constraint.prototype.init.call(this);
|
|
354
|
+
this.disabled = cx.disabledComputing;
|
|
355
|
+
},
|
|
356
|
+
addType: function(fn, weight) {
|
|
357
|
+
if (!(fn instanceof Fn)) return;
|
|
358
|
+
for (var i = 0; i < this.args.length; ++i) {
|
|
359
|
+
if (i < fn.args.length) this.args[i].propagate(fn.args[i], weight);
|
|
360
|
+
if (fn.arguments) this.args[i].propagate(fn.arguments, weight);
|
|
361
|
+
}
|
|
362
|
+
this.self.propagate(fn.self, this.self == cx.topScope ? WG_GLOBAL_THIS : weight);
|
|
363
|
+
var compute = fn.computeRet;
|
|
364
|
+
if (compute) for (var d = this.disabled; d; d = d.prev)
|
|
365
|
+
if (d.fn == fn || fn.originNode && d.fn.originNode == fn.originNode) compute = null;
|
|
366
|
+
if (compute)
|
|
367
|
+
compute(this.self, this.args, this.argNodes).propagate(this.retval, weight);
|
|
368
|
+
else
|
|
369
|
+
fn.retval.propagate(this.retval, weight);
|
|
370
|
+
},
|
|
371
|
+
typeHint: function() {
|
|
372
|
+
var names = [];
|
|
373
|
+
for (var i = 0; i < this.args.length; ++i) names.push("?");
|
|
374
|
+
return new Fn(null, this.self, this.args, names, ANull);
|
|
375
|
+
},
|
|
376
|
+
propagatesTo: function() {
|
|
377
|
+
return {target: this.retval, pathExt: ".!ret"};
|
|
378
|
+
}
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
var HasMethodCall = constraint("propName, args, argNodes, retval", {
|
|
382
|
+
init: function() {
|
|
383
|
+
Constraint.prototype.init.call(this);
|
|
384
|
+
this.disabled = cx.disabledComputing;
|
|
385
|
+
},
|
|
386
|
+
addType: function(obj, weight) {
|
|
387
|
+
var callee = new IsCallee(obj, this.args, this.argNodes, this.retval);
|
|
388
|
+
callee.disabled = this.disabled;
|
|
389
|
+
obj.getProp(this.propName).propagate(callee, weight);
|
|
390
|
+
},
|
|
391
|
+
propHint: function() { return this.propName; }
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
var IsCtor = exports.IsCtor = constraint("target, noReuse", {
|
|
395
|
+
addType: function(f, weight) {
|
|
396
|
+
if (!(f instanceof Fn)) return;
|
|
397
|
+
if (cx.parent && !cx.parent.options.reuseInstances) this.noReuse = true;
|
|
398
|
+
f.getProp("prototype").propagate(new IsProto(this.noReuse ? false : f, this.target), weight);
|
|
399
|
+
}
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
var getInstance = exports.getInstance = function(obj, ctor) {
|
|
403
|
+
if (ctor === false) return new Obj(obj);
|
|
404
|
+
|
|
405
|
+
if (!ctor) ctor = obj.hasCtor;
|
|
406
|
+
if (!obj.instances) obj.instances = [];
|
|
407
|
+
for (var i = 0; i < obj.instances.length; ++i) {
|
|
408
|
+
var cur = obj.instances[i];
|
|
409
|
+
if (cur.ctor == ctor) return cur.instance;
|
|
410
|
+
}
|
|
411
|
+
var instance = new Obj(obj, ctor && ctor.name);
|
|
412
|
+
instance.origin = obj.origin;
|
|
413
|
+
obj.instances.push({ctor: ctor, instance: instance});
|
|
414
|
+
return instance;
|
|
415
|
+
};
|
|
416
|
+
|
|
417
|
+
var IsProto = exports.IsProto = constraint("ctor, target", {
|
|
418
|
+
addType: function(o, _weight) {
|
|
419
|
+
if (!(o instanceof Obj)) return;
|
|
420
|
+
if ((this.count = (this.count || 0) + 1) > 8) return;
|
|
421
|
+
if (o == cx.protos.Array)
|
|
422
|
+
this.target.addType(new Arr);
|
|
423
|
+
else
|
|
424
|
+
this.target.addType(getInstance(o, this.ctor));
|
|
425
|
+
}
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
var FnPrototype = constraint("fn", {
|
|
429
|
+
addType: function(o, _weight) {
|
|
430
|
+
if (o instanceof Obj && !o.hasCtor) {
|
|
431
|
+
o.hasCtor = this.fn;
|
|
432
|
+
var adder = new SpeculativeThis(o, this.fn);
|
|
433
|
+
adder.addType(this.fn);
|
|
434
|
+
o.forAllProps(function(_prop, val, local) {
|
|
435
|
+
if (local) val.propagate(adder);
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
var IsAdded = constraint("other, target", {
|
|
442
|
+
addType: function(type, weight) {
|
|
443
|
+
if (type == cx.str)
|
|
444
|
+
this.target.addType(cx.str, weight);
|
|
445
|
+
else if (type == cx.num && this.other.hasType(cx.num))
|
|
446
|
+
this.target.addType(cx.num, weight);
|
|
447
|
+
},
|
|
448
|
+
typeHint: function() { return this.other; }
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
var IfObj = exports.IfObj = constraint("target", {
|
|
452
|
+
addType: function(t, weight) {
|
|
453
|
+
if (t instanceof Obj) this.target.addType(t, weight);
|
|
454
|
+
},
|
|
455
|
+
propagatesTo: function() { return this.target; }
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
var SpeculativeThis = constraint("obj, ctor", {
|
|
459
|
+
addType: function(tp) {
|
|
460
|
+
if (tp instanceof Fn && tp.self && tp.self.isEmpty())
|
|
461
|
+
tp.self.addType(getInstance(this.obj, this.ctor), WG_SPECULATIVE_THIS);
|
|
462
|
+
}
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
var Muffle = constraint("inner, weight", {
|
|
466
|
+
addType: function(tp, weight) {
|
|
467
|
+
this.inner.addType(tp, Math.min(weight, this.weight));
|
|
468
|
+
},
|
|
469
|
+
propagatesTo: function() { return this.inner.propagatesTo(); },
|
|
470
|
+
typeHint: function() { return this.inner.typeHint(); },
|
|
471
|
+
propHint: function() { return this.inner.propHint(); }
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
// TYPE OBJECTS
|
|
475
|
+
|
|
476
|
+
var Type = exports.Type = function() {};
|
|
477
|
+
Type.prototype = extend(ANull, {
|
|
478
|
+
constructor: Type,
|
|
479
|
+
propagate: function(c, w) { c.addType(this, w); },
|
|
480
|
+
hasType: function(other) { return other == this; },
|
|
481
|
+
isEmpty: function() { return false; },
|
|
482
|
+
typeHint: function() { return this; },
|
|
483
|
+
getType: function() { return this; }
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
var Prim = exports.Prim = function(proto, name) { this.name = name; this.proto = proto; };
|
|
487
|
+
Prim.prototype = extend(Type.prototype, {
|
|
488
|
+
constructor: Prim,
|
|
489
|
+
toString: function() { return this.name; },
|
|
490
|
+
getProp: function(prop) {return this.proto.hasProp(prop) || ANull;},
|
|
491
|
+
gatherProperties: function(f, depth) {
|
|
492
|
+
if (this.proto) this.proto.gatherProperties(f, depth);
|
|
493
|
+
}
|
|
494
|
+
});
|
|
495
|
+
|
|
496
|
+
var Obj = exports.Obj = function(proto, name) {
|
|
497
|
+
if (!this.props) this.props = Object.create(null);
|
|
498
|
+
this.proto = proto === true ? cx.protos.Object : proto;
|
|
499
|
+
if (proto && !name && proto.name && !(this instanceof Fn)) {
|
|
500
|
+
var match = /^(.*)\.prototype$/.exec(this.proto.name);
|
|
501
|
+
if (match) name = match[1];
|
|
502
|
+
}
|
|
503
|
+
this.name = name;
|
|
504
|
+
this.maybeProps = null;
|
|
505
|
+
this.origin = cx.curOrigin;
|
|
506
|
+
};
|
|
507
|
+
Obj.prototype = extend(Type.prototype, {
|
|
508
|
+
constructor: Obj,
|
|
509
|
+
toString: function(maxDepth) {
|
|
510
|
+
if (maxDepth == null) maxDepth = 0;
|
|
511
|
+
if (maxDepth <= 0 && this.name) return this.name;
|
|
512
|
+
var props = [], etc = false;
|
|
513
|
+
for (var prop in this.props) if (prop != "<i>") {
|
|
514
|
+
if (props.length > 5) { etc = true; break; }
|
|
515
|
+
if (maxDepth)
|
|
516
|
+
props.push(prop + ": " + toString(this.props[prop], maxDepth - 1, this));
|
|
517
|
+
else
|
|
518
|
+
props.push(prop);
|
|
519
|
+
}
|
|
520
|
+
props.sort();
|
|
521
|
+
if (etc) props.push("...");
|
|
522
|
+
return "{" + props.join(", ") + "}";
|
|
523
|
+
},
|
|
524
|
+
hasProp: function(prop, searchProto) {
|
|
525
|
+
var found = this.props[prop];
|
|
526
|
+
if (searchProto !== false)
|
|
527
|
+
for (var p = this.proto; p && !found; p = p.proto) found = p.props[prop];
|
|
528
|
+
return found;
|
|
529
|
+
},
|
|
530
|
+
defProp: function(prop, originNode) {
|
|
531
|
+
var found = this.hasProp(prop, false);
|
|
532
|
+
if (found) {
|
|
533
|
+
if (originNode && !found.originNode) found.originNode = originNode;
|
|
534
|
+
return found;
|
|
535
|
+
}
|
|
536
|
+
if (prop == "__proto__" || prop == "✖") return ANull;
|
|
537
|
+
|
|
538
|
+
var av = this.maybeProps && this.maybeProps[prop];
|
|
539
|
+
if (av) {
|
|
540
|
+
delete this.maybeProps[prop];
|
|
541
|
+
this.maybeUnregProtoPropHandler();
|
|
542
|
+
} else {
|
|
543
|
+
av = new AVal;
|
|
544
|
+
av.propertyOf = this;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
this.props[prop] = av;
|
|
548
|
+
av.originNode = originNode;
|
|
549
|
+
av.origin = cx.curOrigin;
|
|
550
|
+
this.broadcastProp(prop, av, true);
|
|
551
|
+
return av;
|
|
552
|
+
},
|
|
553
|
+
getProp: function(prop) {
|
|
554
|
+
var found = this.hasProp(prop, true) || (this.maybeProps && this.maybeProps[prop]);
|
|
555
|
+
if (found) return found;
|
|
556
|
+
if (prop == "__proto__" || prop == "✖") return ANull;
|
|
557
|
+
var av = this.ensureMaybeProps()[prop] = new AVal;
|
|
558
|
+
av.propertyOf = this;
|
|
559
|
+
return av;
|
|
560
|
+
},
|
|
561
|
+
broadcastProp: function(prop, val, local) {
|
|
562
|
+
if (local) {
|
|
563
|
+
this.signal("addProp", prop, val);
|
|
564
|
+
// If this is a scope, it shouldn't be registered
|
|
565
|
+
if (!(this instanceof Scope)) registerProp(prop, this);
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
if (this.onNewProp) for (var i = 0; i < this.onNewProp.length; ++i) {
|
|
569
|
+
var h = this.onNewProp[i];
|
|
570
|
+
h.onProtoProp ? h.onProtoProp(prop, val, local) : h(prop, val, local);
|
|
571
|
+
}
|
|
572
|
+
},
|
|
573
|
+
onProtoProp: function(prop, val, _local) {
|
|
574
|
+
var maybe = this.maybeProps && this.maybeProps[prop];
|
|
575
|
+
if (maybe) {
|
|
576
|
+
delete this.maybeProps[prop];
|
|
577
|
+
this.maybeUnregProtoPropHandler();
|
|
578
|
+
this.proto.getProp(prop).propagate(maybe);
|
|
579
|
+
}
|
|
580
|
+
this.broadcastProp(prop, val, false);
|
|
581
|
+
},
|
|
582
|
+
ensureMaybeProps: function() {
|
|
583
|
+
if (!this.maybeProps) {
|
|
584
|
+
if (this.proto) this.proto.forAllProps(this);
|
|
585
|
+
this.maybeProps = Object.create(null);
|
|
586
|
+
}
|
|
587
|
+
return this.maybeProps;
|
|
588
|
+
},
|
|
589
|
+
removeProp: function(prop) {
|
|
590
|
+
var av = this.props[prop];
|
|
591
|
+
delete this.props[prop];
|
|
592
|
+
this.ensureMaybeProps()[prop] = av;
|
|
593
|
+
av.types.length = 0;
|
|
594
|
+
},
|
|
595
|
+
forAllProps: function(c) {
|
|
596
|
+
if (!this.onNewProp) {
|
|
597
|
+
this.onNewProp = [];
|
|
598
|
+
if (this.proto) this.proto.forAllProps(this);
|
|
599
|
+
}
|
|
600
|
+
this.onNewProp.push(c);
|
|
601
|
+
for (var o = this; o; o = o.proto) for (var prop in o.props) {
|
|
602
|
+
if (c.onProtoProp)
|
|
603
|
+
c.onProtoProp(prop, o.props[prop], o == this);
|
|
604
|
+
else
|
|
605
|
+
c(prop, o.props[prop], o == this);
|
|
606
|
+
}
|
|
607
|
+
},
|
|
608
|
+
maybeUnregProtoPropHandler: function() {
|
|
609
|
+
if (this.maybeProps) {
|
|
610
|
+
for (var _n in this.maybeProps) return;
|
|
611
|
+
this.maybeProps = null;
|
|
612
|
+
}
|
|
613
|
+
if (!this.proto || this.onNewProp && this.onNewProp.length) return;
|
|
614
|
+
this.proto.unregPropHandler(this);
|
|
615
|
+
},
|
|
616
|
+
unregPropHandler: function(handler) {
|
|
617
|
+
for (var i = 0; i < this.onNewProp.length; ++i)
|
|
618
|
+
if (this.onNewProp[i] == handler) { this.onNewProp.splice(i, 1); break; }
|
|
619
|
+
this.maybeUnregProtoPropHandler();
|
|
620
|
+
},
|
|
621
|
+
gatherProperties: function(f, depth) {
|
|
622
|
+
for (var prop in this.props) if (prop != "<i>")
|
|
623
|
+
f(prop, this, depth);
|
|
624
|
+
if (this.proto) this.proto.gatherProperties(f, depth + 1);
|
|
625
|
+
},
|
|
626
|
+
getObjType: function() { return this; }
|
|
627
|
+
});
|
|
628
|
+
|
|
629
|
+
var Fn = exports.Fn = function(name, self, args, argNames, retval) {
|
|
630
|
+
Obj.call(this, cx.protos.Function, name);
|
|
631
|
+
this.self = self;
|
|
632
|
+
this.args = args;
|
|
633
|
+
this.argNames = argNames;
|
|
634
|
+
this.retval = retval;
|
|
635
|
+
};
|
|
636
|
+
Fn.prototype = extend(Obj.prototype, {
|
|
637
|
+
constructor: Fn,
|
|
638
|
+
toString: function(maxDepth) {
|
|
639
|
+
if (maxDepth == null) maxDepth = 0;
|
|
640
|
+
var str = "fn(";
|
|
641
|
+
for (var i = 0; i < this.args.length; ++i) {
|
|
642
|
+
if (i) str += ", ";
|
|
643
|
+
var name = this.argNames[i];
|
|
644
|
+
if (name && name != "?") str += name + ": ";
|
|
645
|
+
str += maxDepth > -3 ? toString(this.args[i], maxDepth - 1, this) : "?";
|
|
646
|
+
}
|
|
647
|
+
str += ")";
|
|
648
|
+
if (!this.retval.isEmpty())
|
|
649
|
+
str += " -> " + (maxDepth > -3 ? toString(this.retval, maxDepth - 1, this) : "?");
|
|
650
|
+
return str;
|
|
651
|
+
},
|
|
652
|
+
getProp: function(prop) {
|
|
653
|
+
if (prop == "prototype") {
|
|
654
|
+
var known = this.hasProp(prop, false);
|
|
655
|
+
if (!known) {
|
|
656
|
+
known = this.defProp(prop);
|
|
657
|
+
var proto = new Obj(true, this.name && this.name + ".prototype");
|
|
658
|
+
proto.origin = this.origin;
|
|
659
|
+
known.addType(proto, WG_MADEUP_PROTO);
|
|
660
|
+
}
|
|
661
|
+
return known;
|
|
662
|
+
}
|
|
663
|
+
return Obj.prototype.getProp.call(this, prop);
|
|
664
|
+
},
|
|
665
|
+
defProp: function(prop, originNode) {
|
|
666
|
+
if (prop == "prototype") {
|
|
667
|
+
var found = this.hasProp(prop, false);
|
|
668
|
+
if (found) return found;
|
|
669
|
+
found = Obj.prototype.defProp.call(this, prop, originNode);
|
|
670
|
+
found.origin = this.origin;
|
|
671
|
+
found.propagate(new FnPrototype(this));
|
|
672
|
+
return found;
|
|
673
|
+
}
|
|
674
|
+
return Obj.prototype.defProp.call(this, prop, originNode);
|
|
675
|
+
},
|
|
676
|
+
getFunctionType: function() { return this; }
|
|
677
|
+
});
|
|
678
|
+
|
|
679
|
+
var Arr = exports.Arr = function(contentType) {
|
|
680
|
+
Obj.call(this, cx.protos.Array);
|
|
681
|
+
var content = this.defProp("<i>");
|
|
682
|
+
if (contentType) contentType.propagate(content);
|
|
683
|
+
};
|
|
684
|
+
Arr.prototype = extend(Obj.prototype, {
|
|
685
|
+
constructor: Arr,
|
|
686
|
+
toString: function(maxDepth) {
|
|
687
|
+
if (maxDepth == null) maxDepth = 0;
|
|
688
|
+
return "[" + (maxDepth > -3 ? toString(this.getProp("<i>"), maxDepth - 1, this) : "?") + "]";
|
|
689
|
+
}
|
|
690
|
+
});
|
|
691
|
+
|
|
692
|
+
// THE PROPERTY REGISTRY
|
|
693
|
+
|
|
694
|
+
function registerProp(prop, obj) {
|
|
695
|
+
var data = cx.props[prop] || (cx.props[prop] = []);
|
|
696
|
+
data.push(obj);
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
function objsWithProp(prop) {
|
|
700
|
+
return cx.props[prop];
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
// INFERENCE CONTEXT
|
|
704
|
+
|
|
705
|
+
exports.Context = function(defs, parent) {
|
|
706
|
+
this.parent = parent;
|
|
707
|
+
this.props = Object.create(null);
|
|
708
|
+
this.protos = Object.create(null);
|
|
709
|
+
this.origins = [];
|
|
710
|
+
this.curOrigin = "ecma5";
|
|
711
|
+
this.paths = Object.create(null);
|
|
712
|
+
this.definitions = Object.create(null);
|
|
713
|
+
this.purgeGen = 0;
|
|
714
|
+
this.workList = null;
|
|
715
|
+
this.disabledComputing = null;
|
|
716
|
+
|
|
717
|
+
exports.withContext(this, function() {
|
|
718
|
+
cx.protos.Object = new Obj(null, "Object.prototype");
|
|
719
|
+
cx.topScope = new Scope();
|
|
720
|
+
cx.topScope.name = "<top>";
|
|
721
|
+
cx.protos.Array = new Obj(true, "Array.prototype");
|
|
722
|
+
cx.protos.Function = new Obj(true, "Function.prototype");
|
|
723
|
+
cx.protos.RegExp = new Obj(true, "RegExp.prototype");
|
|
724
|
+
cx.protos.String = new Obj(true, "String.prototype");
|
|
725
|
+
cx.protos.Number = new Obj(true, "Number.prototype");
|
|
726
|
+
cx.protos.Boolean = new Obj(true, "Boolean.prototype");
|
|
727
|
+
cx.str = new Prim(cx.protos.String, "string");
|
|
728
|
+
cx.bool = new Prim(cx.protos.Boolean, "bool");
|
|
729
|
+
cx.num = new Prim(cx.protos.Number, "number");
|
|
730
|
+
cx.curOrigin = null;
|
|
731
|
+
|
|
732
|
+
if (defs) for (var i = 0; i < defs.length; ++i)
|
|
733
|
+
def.load(defs[i]);
|
|
734
|
+
});
|
|
735
|
+
};
|
|
736
|
+
|
|
737
|
+
var cx = null;
|
|
738
|
+
exports.cx = function() { return cx; };
|
|
739
|
+
|
|
740
|
+
exports.withContext = function(context, f) {
|
|
741
|
+
var old = cx;
|
|
742
|
+
cx = context;
|
|
743
|
+
try { return f(); }
|
|
744
|
+
finally { cx = old; }
|
|
745
|
+
};
|
|
746
|
+
|
|
747
|
+
exports.TimedOut = function() {
|
|
748
|
+
this.message = "Timed out";
|
|
749
|
+
this.stack = (new Error()).stack;
|
|
750
|
+
};
|
|
751
|
+
exports.TimedOut.prototype = Object.create(Error.prototype);
|
|
752
|
+
exports.TimedOut.prototype.name = "infer.TimedOut";
|
|
753
|
+
|
|
754
|
+
var timeout;
|
|
755
|
+
exports.withTimeout = function(ms, f) {
|
|
756
|
+
var end = +new Date + ms;
|
|
757
|
+
var oldEnd = timeout;
|
|
758
|
+
if (oldEnd && oldEnd < end) return f();
|
|
759
|
+
timeout = end;
|
|
760
|
+
try { return f(); }
|
|
761
|
+
finally { timeout = oldEnd; }
|
|
762
|
+
};
|
|
763
|
+
|
|
764
|
+
exports.addOrigin = function(origin) {
|
|
765
|
+
if (cx.origins.indexOf(origin) < 0) cx.origins.push(origin);
|
|
766
|
+
};
|
|
767
|
+
|
|
768
|
+
var baseMaxWorkDepth = 20, reduceMaxWorkDepth = 0.0001;
|
|
769
|
+
function withWorklist(f) {
|
|
770
|
+
if (cx.workList) return f(cx.workList);
|
|
771
|
+
|
|
772
|
+
var list = [], depth = 0;
|
|
773
|
+
var add = cx.workList = function(type, target, weight) {
|
|
774
|
+
if (depth < baseMaxWorkDepth - reduceMaxWorkDepth * list.length)
|
|
775
|
+
list.push(type, target, weight, depth);
|
|
776
|
+
};
|
|
777
|
+
try {
|
|
778
|
+
var ret = f(add);
|
|
779
|
+
for (var i = 0; i < list.length; i += 4) {
|
|
780
|
+
if (timeout && +new Date >= timeout)
|
|
781
|
+
throw new exports.TimedOut();
|
|
782
|
+
depth = list[i + 3] + 1;
|
|
783
|
+
list[i + 1].addType(list[i], list[i + 2]);
|
|
784
|
+
}
|
|
785
|
+
return ret;
|
|
786
|
+
} finally {
|
|
787
|
+
cx.workList = null;
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
// SCOPES
|
|
792
|
+
|
|
793
|
+
var Scope = exports.Scope = function(prev) {
|
|
794
|
+
Obj.call(this, prev || true);
|
|
795
|
+
this.prev = prev;
|
|
796
|
+
};
|
|
797
|
+
Scope.prototype = extend(Obj.prototype, {
|
|
798
|
+
constructor: Scope,
|
|
799
|
+
defVar: function(name, originNode) {
|
|
800
|
+
for (var s = this; ; s = s.proto) {
|
|
801
|
+
var found = s.props[name];
|
|
802
|
+
if (found) return found;
|
|
803
|
+
if (!s.prev) return s.defProp(name, originNode);
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
});
|
|
807
|
+
|
|
808
|
+
// RETVAL COMPUTATION HEURISTICS
|
|
809
|
+
|
|
810
|
+
function maybeInstantiate(scope, score) {
|
|
811
|
+
if (scope.fnType)
|
|
812
|
+
scope.fnType.instantiateScore = (scope.fnType.instantiateScore || 0) + score;
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
var NotSmaller = {};
|
|
816
|
+
function nodeSmallerThan(node, n) {
|
|
817
|
+
try {
|
|
818
|
+
walk.simple(node, {Expression: function() { if (--n <= 0) throw NotSmaller; }});
|
|
819
|
+
return true;
|
|
820
|
+
} catch(e) {
|
|
821
|
+
if (e == NotSmaller) return false;
|
|
822
|
+
throw e;
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
function maybeTagAsInstantiated(node, scope) {
|
|
827
|
+
var score = scope.fnType.instantiateScore;
|
|
828
|
+
if (!cx.disabledComputing && score && scope.fnType.args.length && nodeSmallerThan(node, score * 5)) {
|
|
829
|
+
maybeInstantiate(scope.prev, score / 2);
|
|
830
|
+
setFunctionInstantiated(node, scope);
|
|
831
|
+
return true;
|
|
832
|
+
} else {
|
|
833
|
+
scope.fnType.instantiateScore = null;
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
function setFunctionInstantiated(node, scope) {
|
|
838
|
+
var fn = scope.fnType;
|
|
839
|
+
// Disconnect the arg avals, so that we can add info to them without side effects
|
|
840
|
+
for (var i = 0; i < fn.args.length; ++i) fn.args[i] = new AVal;
|
|
841
|
+
fn.self = new AVal;
|
|
842
|
+
fn.computeRet = function(self, args) {
|
|
843
|
+
// Prevent recursion
|
|
844
|
+
return withDisabledComputing(fn, function() {
|
|
845
|
+
var oldOrigin = cx.curOrigin;
|
|
846
|
+
cx.curOrigin = fn.origin;
|
|
847
|
+
var scopeCopy = new Scope(scope.prev);
|
|
848
|
+
scopeCopy.originNode = scope.originNode;
|
|
849
|
+
for (var v in scope.props) {
|
|
850
|
+
var local = scopeCopy.defProp(v, scope.props[v].originNode);
|
|
851
|
+
for (var i = 0; i < args.length; ++i) if (fn.argNames[i] == v && i < args.length)
|
|
852
|
+
args[i].propagate(local);
|
|
853
|
+
}
|
|
854
|
+
var argNames = fn.argNames.length != args.length ? fn.argNames.slice(0, args.length) : fn.argNames;
|
|
855
|
+
while (argNames.length < args.length) argNames.push("?");
|
|
856
|
+
scopeCopy.fnType = new Fn(fn.name, self, args, argNames, ANull);
|
|
857
|
+
scopeCopy.fnType.originNode = fn.originNode;
|
|
858
|
+
if (fn.arguments) {
|
|
859
|
+
var argset = scopeCopy.fnType.arguments = new AVal;
|
|
860
|
+
scopeCopy.defProp("arguments").addType(new Arr(argset));
|
|
861
|
+
for (var i = 0; i < args.length; ++i) args[i].propagate(argset);
|
|
862
|
+
}
|
|
863
|
+
node.body.scope = scopeCopy;
|
|
864
|
+
walk.recursive(node.body, scopeCopy, null, scopeGatherer);
|
|
865
|
+
walk.recursive(node.body, scopeCopy, null, inferWrapper);
|
|
866
|
+
cx.curOrigin = oldOrigin;
|
|
867
|
+
return scopeCopy.fnType.retval;
|
|
868
|
+
});
|
|
869
|
+
};
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
function maybeTagAsGeneric(scope) {
|
|
873
|
+
var fn = scope.fnType, target = fn.retval;
|
|
874
|
+
if (target == ANull) return;
|
|
875
|
+
var targetInner, asArray;
|
|
876
|
+
if (!target.isEmpty() && (targetInner = target.getType()) instanceof Arr)
|
|
877
|
+
target = asArray = targetInner.getProp("<i>");
|
|
878
|
+
|
|
879
|
+
function explore(aval, path, depth) {
|
|
880
|
+
if (depth > 3 || !aval.forward) return;
|
|
881
|
+
for (var i = 0; i < aval.forward.length; ++i) {
|
|
882
|
+
var prop = aval.forward[i].propagatesTo();
|
|
883
|
+
if (!prop) continue;
|
|
884
|
+
var newPath = path, dest;
|
|
885
|
+
if (prop instanceof AVal) {
|
|
886
|
+
dest = prop;
|
|
887
|
+
} else if (prop.target instanceof AVal) {
|
|
888
|
+
newPath += prop.pathExt;
|
|
889
|
+
dest = prop.target;
|
|
890
|
+
} else continue;
|
|
891
|
+
if (dest == target) return newPath;
|
|
892
|
+
var found = explore(dest, newPath, depth + 1);
|
|
893
|
+
if (found) return found;
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
var foundPath = explore(fn.self, "!this", 0);
|
|
898
|
+
for (var i = 0; !foundPath && i < fn.args.length; ++i)
|
|
899
|
+
foundPath = explore(fn.args[i], "!" + i, 0);
|
|
900
|
+
|
|
901
|
+
if (foundPath) {
|
|
902
|
+
if (asArray) foundPath = "[" + foundPath + "]";
|
|
903
|
+
var p = new def.TypeParser(foundPath);
|
|
904
|
+
var parsed = p.parseType(true);
|
|
905
|
+
fn.computeRet = parsed.apply ? parsed : function() { return parsed; };
|
|
906
|
+
fn.computeRetSource = foundPath;
|
|
907
|
+
return true;
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
// SCOPE GATHERING PASS
|
|
912
|
+
|
|
913
|
+
function addVar(scope, nameNode) {
|
|
914
|
+
return scope.defProp(nameNode.name, nameNode);
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
var scopeGatherer = walk.make({
|
|
918
|
+
Function: function(node, scope, c) {
|
|
919
|
+
var inner = node.body.scope = new Scope(scope);
|
|
920
|
+
inner.originNode = node;
|
|
921
|
+
var argVals = [], argNames = [];
|
|
922
|
+
for (var i = 0; i < node.params.length; ++i) {
|
|
923
|
+
var param = node.params[i];
|
|
924
|
+
argNames.push(param.name);
|
|
925
|
+
argVals.push(addVar(inner, param));
|
|
926
|
+
}
|
|
927
|
+
inner.fnType = new Fn(node.id && node.id.name, new AVal, argVals, argNames, ANull);
|
|
928
|
+
inner.fnType.originNode = node;
|
|
929
|
+
if (node.id) {
|
|
930
|
+
var decl = node.type == "FunctionDeclaration";
|
|
931
|
+
addVar(decl ? scope : inner, node.id);
|
|
932
|
+
}
|
|
933
|
+
c(node.body, inner, "ScopeBody");
|
|
934
|
+
},
|
|
935
|
+
TryStatement: function(node, scope, c) {
|
|
936
|
+
c(node.block, scope, "Statement");
|
|
937
|
+
if (node.handler) {
|
|
938
|
+
var v = addVar(scope, node.handler.param);
|
|
939
|
+
c(node.handler.body, scope, "ScopeBody");
|
|
940
|
+
var e5 = cx.definitions.ecma5;
|
|
941
|
+
if (e5 && v.isEmpty()) getInstance(e5["Error.prototype"]).propagate(v, WG_CATCH_ERROR);
|
|
942
|
+
}
|
|
943
|
+
if (node.finalizer) c(node.finalizer, scope, "Statement");
|
|
944
|
+
},
|
|
945
|
+
VariableDeclaration: function(node, scope, c) {
|
|
946
|
+
for (var i = 0; i < node.declarations.length; ++i) {
|
|
947
|
+
var decl = node.declarations[i];
|
|
948
|
+
addVar(scope, decl.id);
|
|
949
|
+
if (decl.init) c(decl.init, scope, "Expression");
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
});
|
|
953
|
+
|
|
954
|
+
// CONSTRAINT GATHERING PASS
|
|
955
|
+
|
|
956
|
+
function propName(node, scope, c) {
|
|
957
|
+
var prop = node.property;
|
|
958
|
+
if (!node.computed) return prop.name;
|
|
959
|
+
if (prop.type == "Literal" && typeof prop.value == "string") return prop.value;
|
|
960
|
+
if (c) infer(prop, scope, c, ANull);
|
|
961
|
+
return "<i>";
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
function unopResultType(op) {
|
|
965
|
+
switch (op) {
|
|
966
|
+
case "+": case "-": case "~": return cx.num;
|
|
967
|
+
case "!": return cx.bool;
|
|
968
|
+
case "typeof": return cx.str;
|
|
969
|
+
case "void": case "delete": return ANull;
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
function binopIsBoolean(op) {
|
|
973
|
+
switch (op) {
|
|
974
|
+
case "==": case "!=": case "===": case "!==": case "<": case ">": case ">=": case "<=":
|
|
975
|
+
case "in": case "instanceof": return true;
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
function literalType(node) {
|
|
979
|
+
if (node.regex) return getInstance(cx.protos.RegExp);
|
|
980
|
+
switch (typeof node.value) {
|
|
981
|
+
case "boolean": return cx.bool;
|
|
982
|
+
case "number": return cx.num;
|
|
983
|
+
case "string": return cx.str;
|
|
984
|
+
case "object":
|
|
985
|
+
case "function":
|
|
986
|
+
if (!node.value) return ANull;
|
|
987
|
+
return getInstance(cx.protos.RegExp);
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
function ret(f) {
|
|
992
|
+
return function(node, scope, c, out, name) {
|
|
993
|
+
var r = f(node, scope, c, name);
|
|
994
|
+
if (out) r.propagate(out);
|
|
995
|
+
return r;
|
|
996
|
+
};
|
|
997
|
+
}
|
|
998
|
+
function fill(f) {
|
|
999
|
+
return function(node, scope, c, out, name) {
|
|
1000
|
+
if (!out) out = new AVal;
|
|
1001
|
+
f(node, scope, c, out, name);
|
|
1002
|
+
return out;
|
|
1003
|
+
};
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
var inferExprVisitor = {
|
|
1007
|
+
ArrayExpression: ret(function(node, scope, c) {
|
|
1008
|
+
var eltval = new AVal;
|
|
1009
|
+
for (var i = 0; i < node.elements.length; ++i) {
|
|
1010
|
+
var elt = node.elements[i];
|
|
1011
|
+
if (elt) infer(elt, scope, c, eltval);
|
|
1012
|
+
}
|
|
1013
|
+
return new Arr(eltval);
|
|
1014
|
+
}),
|
|
1015
|
+
ObjectExpression: ret(function(node, scope, c, name) {
|
|
1016
|
+
var obj = node.objType = new Obj(true, name);
|
|
1017
|
+
obj.originNode = node;
|
|
1018
|
+
|
|
1019
|
+
for (var i = 0; i < node.properties.length; ++i) {
|
|
1020
|
+
var prop = node.properties[i], key = prop.key, name;
|
|
1021
|
+
if (prop.value.name == "✖") continue;
|
|
1022
|
+
|
|
1023
|
+
if (key.type == "Identifier") {
|
|
1024
|
+
name = key.name;
|
|
1025
|
+
} else if (typeof key.value == "string") {
|
|
1026
|
+
name = key.value;
|
|
1027
|
+
}
|
|
1028
|
+
if (!name || prop.kind == "set") {
|
|
1029
|
+
infer(prop.value, scope, c, ANull);
|
|
1030
|
+
continue;
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
var val = obj.defProp(name, key), out = val;
|
|
1034
|
+
val.initializer = true;
|
|
1035
|
+
if (prop.kind == "get")
|
|
1036
|
+
out = new IsCallee(obj, [], null, val);
|
|
1037
|
+
infer(prop.value, scope, c, out, name);
|
|
1038
|
+
}
|
|
1039
|
+
return obj;
|
|
1040
|
+
}),
|
|
1041
|
+
FunctionExpression: ret(function(node, scope, c, name) {
|
|
1042
|
+
var inner = node.body.scope, fn = inner.fnType;
|
|
1043
|
+
if (name && !fn.name) fn.name = name;
|
|
1044
|
+
c(node.body, scope, "ScopeBody");
|
|
1045
|
+
maybeTagAsInstantiated(node, inner) || maybeTagAsGeneric(inner);
|
|
1046
|
+
if (node.id) inner.getProp(node.id.name).addType(fn);
|
|
1047
|
+
return fn;
|
|
1048
|
+
}),
|
|
1049
|
+
SequenceExpression: ret(function(node, scope, c) {
|
|
1050
|
+
for (var i = 0, l = node.expressions.length - 1; i < l; ++i)
|
|
1051
|
+
infer(node.expressions[i], scope, c, ANull);
|
|
1052
|
+
return infer(node.expressions[l], scope, c);
|
|
1053
|
+
}),
|
|
1054
|
+
UnaryExpression: ret(function(node, scope, c) {
|
|
1055
|
+
infer(node.argument, scope, c, ANull);
|
|
1056
|
+
return unopResultType(node.operator);
|
|
1057
|
+
}),
|
|
1058
|
+
UpdateExpression: ret(function(node, scope, c) {
|
|
1059
|
+
infer(node.argument, scope, c, ANull);
|
|
1060
|
+
return cx.num;
|
|
1061
|
+
}),
|
|
1062
|
+
BinaryExpression: ret(function(node, scope, c) {
|
|
1063
|
+
if (node.operator == "+") {
|
|
1064
|
+
var lhs = infer(node.left, scope, c);
|
|
1065
|
+
var rhs = infer(node.right, scope, c);
|
|
1066
|
+
if (lhs.hasType(cx.str) || rhs.hasType(cx.str)) return cx.str;
|
|
1067
|
+
if (lhs.hasType(cx.num) && rhs.hasType(cx.num)) return cx.num;
|
|
1068
|
+
var result = new AVal;
|
|
1069
|
+
lhs.propagate(new IsAdded(rhs, result));
|
|
1070
|
+
rhs.propagate(new IsAdded(lhs, result));
|
|
1071
|
+
return result;
|
|
1072
|
+
} else {
|
|
1073
|
+
infer(node.left, scope, c, ANull);
|
|
1074
|
+
infer(node.right, scope, c, ANull);
|
|
1075
|
+
return binopIsBoolean(node.operator) ? cx.bool : cx.num;
|
|
1076
|
+
}
|
|
1077
|
+
}),
|
|
1078
|
+
AssignmentExpression: ret(function(node, scope, c) {
|
|
1079
|
+
var rhs, name, pName;
|
|
1080
|
+
if (node.left.type == "MemberExpression") {
|
|
1081
|
+
pName = propName(node.left, scope, c);
|
|
1082
|
+
if (node.left.object.type == "Identifier")
|
|
1083
|
+
name = node.left.object.name + "." + pName;
|
|
1084
|
+
} else {
|
|
1085
|
+
name = node.left.name;
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
if (node.operator != "=" && node.operator != "+=") {
|
|
1089
|
+
infer(node.right, scope, c, ANull);
|
|
1090
|
+
rhs = cx.num;
|
|
1091
|
+
} else {
|
|
1092
|
+
rhs = infer(node.right, scope, c, null, name);
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
if (node.left.type == "MemberExpression") {
|
|
1096
|
+
var obj = infer(node.left.object, scope, c);
|
|
1097
|
+
if (pName == "prototype") maybeInstantiate(scope, 20);
|
|
1098
|
+
if (pName == "<i>") {
|
|
1099
|
+
// This is a hack to recognize for/in loops that copy
|
|
1100
|
+
// properties, and do the copying ourselves, insofar as we
|
|
1101
|
+
// manage, because such loops tend to be relevant for type
|
|
1102
|
+
// information.
|
|
1103
|
+
var v = node.left.property.name, local = scope.props[v], over = local && local.iteratesOver;
|
|
1104
|
+
if (over) {
|
|
1105
|
+
maybeInstantiate(scope, 20);
|
|
1106
|
+
var fromRight = node.right.type == "MemberExpression" && node.right.computed && node.right.property.name == v;
|
|
1107
|
+
over.forAllProps(function(prop, val, local) {
|
|
1108
|
+
if (local && prop != "prototype" && prop != "<i>")
|
|
1109
|
+
obj.propagate(new PropHasSubset(prop, fromRight ? val : ANull));
|
|
1110
|
+
});
|
|
1111
|
+
return rhs;
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
obj.propagate(new PropHasSubset(pName, rhs, node.left.property));
|
|
1115
|
+
} else { // Identifier
|
|
1116
|
+
rhs.propagate(scope.defVar(node.left.name, node.left));
|
|
1117
|
+
}
|
|
1118
|
+
return rhs;
|
|
1119
|
+
}),
|
|
1120
|
+
LogicalExpression: fill(function(node, scope, c, out) {
|
|
1121
|
+
infer(node.left, scope, c, out);
|
|
1122
|
+
infer(node.right, scope, c, out);
|
|
1123
|
+
}),
|
|
1124
|
+
ConditionalExpression: fill(function(node, scope, c, out) {
|
|
1125
|
+
infer(node.test, scope, c, ANull);
|
|
1126
|
+
infer(node.consequent, scope, c, out);
|
|
1127
|
+
infer(node.alternate, scope, c, out);
|
|
1128
|
+
}),
|
|
1129
|
+
NewExpression: fill(function(node, scope, c, out, name) {
|
|
1130
|
+
if (node.callee.type == "Identifier" && node.callee.name in scope.props)
|
|
1131
|
+
maybeInstantiate(scope, 20);
|
|
1132
|
+
|
|
1133
|
+
for (var i = 0, args = []; i < node.arguments.length; ++i)
|
|
1134
|
+
args.push(infer(node.arguments[i], scope, c));
|
|
1135
|
+
var callee = infer(node.callee, scope, c);
|
|
1136
|
+
var self = new AVal;
|
|
1137
|
+
callee.propagate(new IsCtor(self, name && /\.prototype$/.test(name)));
|
|
1138
|
+
self.propagate(out, WG_NEW_INSTANCE);
|
|
1139
|
+
callee.propagate(new IsCallee(self, args, node.arguments, new IfObj(out)));
|
|
1140
|
+
}),
|
|
1141
|
+
CallExpression: fill(function(node, scope, c, out) {
|
|
1142
|
+
for (var i = 0, args = []; i < node.arguments.length; ++i)
|
|
1143
|
+
args.push(infer(node.arguments[i], scope, c));
|
|
1144
|
+
if (node.callee.type == "MemberExpression") {
|
|
1145
|
+
var self = infer(node.callee.object, scope, c);
|
|
1146
|
+
var pName = propName(node.callee, scope, c);
|
|
1147
|
+
if ((pName == "call" || pName == "apply") &&
|
|
1148
|
+
scope.fnType && scope.fnType.args.indexOf(self) > -1)
|
|
1149
|
+
maybeInstantiate(scope, 30);
|
|
1150
|
+
self.propagate(new HasMethodCall(pName, args, node.arguments, out));
|
|
1151
|
+
} else {
|
|
1152
|
+
var callee = infer(node.callee, scope, c);
|
|
1153
|
+
if (scope.fnType && scope.fnType.args.indexOf(callee) > -1)
|
|
1154
|
+
maybeInstantiate(scope, 30);
|
|
1155
|
+
var knownFn = callee.getFunctionType();
|
|
1156
|
+
if (knownFn && knownFn.instantiateScore && scope.fnType)
|
|
1157
|
+
maybeInstantiate(scope, knownFn.instantiateScore / 5);
|
|
1158
|
+
callee.propagate(new IsCallee(cx.topScope, args, node.arguments, out));
|
|
1159
|
+
}
|
|
1160
|
+
}),
|
|
1161
|
+
MemberExpression: fill(function(node, scope, c, out) {
|
|
1162
|
+
var name = propName(node, scope);
|
|
1163
|
+
var obj = infer(node.object, scope, c);
|
|
1164
|
+
var prop = obj.getProp(name);
|
|
1165
|
+
if (name == "<i>") {
|
|
1166
|
+
var propType = infer(node.property, scope, c);
|
|
1167
|
+
if (!propType.hasType(cx.num))
|
|
1168
|
+
return prop.propagate(out, WG_MULTI_MEMBER);
|
|
1169
|
+
}
|
|
1170
|
+
prop.propagate(out);
|
|
1171
|
+
}),
|
|
1172
|
+
Identifier: ret(function(node, scope) {
|
|
1173
|
+
if (node.name == "arguments" && scope.fnType && !(node.name in scope.props))
|
|
1174
|
+
scope.defProp(node.name, scope.fnType.originNode)
|
|
1175
|
+
.addType(new Arr(scope.fnType.arguments = new AVal));
|
|
1176
|
+
return scope.getProp(node.name);
|
|
1177
|
+
}),
|
|
1178
|
+
ThisExpression: ret(function(_node, scope) {
|
|
1179
|
+
return scope.fnType ? scope.fnType.self : cx.topScope;
|
|
1180
|
+
}),
|
|
1181
|
+
Literal: ret(function(node) {
|
|
1182
|
+
return literalType(node);
|
|
1183
|
+
})
|
|
1184
|
+
};
|
|
1185
|
+
|
|
1186
|
+
function infer(node, scope, c, out, name) {
|
|
1187
|
+
return inferExprVisitor[node.type](node, scope, c, out, name);
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
var inferWrapper = walk.make({
|
|
1191
|
+
Expression: function(node, scope, c) {
|
|
1192
|
+
infer(node, scope, c, ANull);
|
|
1193
|
+
},
|
|
1194
|
+
|
|
1195
|
+
FunctionDeclaration: function(node, scope, c) {
|
|
1196
|
+
var inner = node.body.scope, fn = inner.fnType;
|
|
1197
|
+
c(node.body, scope, "ScopeBody");
|
|
1198
|
+
maybeTagAsInstantiated(node, inner) || maybeTagAsGeneric(inner);
|
|
1199
|
+
var prop = scope.getProp(node.id.name);
|
|
1200
|
+
prop.addType(fn);
|
|
1201
|
+
},
|
|
1202
|
+
|
|
1203
|
+
VariableDeclaration: function(node, scope, c) {
|
|
1204
|
+
for (var i = 0; i < node.declarations.length; ++i) {
|
|
1205
|
+
var decl = node.declarations[i], prop = scope.getProp(decl.id.name);
|
|
1206
|
+
if (decl.init)
|
|
1207
|
+
infer(decl.init, scope, c, prop, decl.id.name);
|
|
1208
|
+
}
|
|
1209
|
+
},
|
|
1210
|
+
|
|
1211
|
+
ReturnStatement: function(node, scope, c) {
|
|
1212
|
+
if (!node.argument) return;
|
|
1213
|
+
var output = ANull;
|
|
1214
|
+
if (scope.fnType) {
|
|
1215
|
+
if (scope.fnType.retval == ANull) scope.fnType.retval = new AVal;
|
|
1216
|
+
output = scope.fnType.retval;
|
|
1217
|
+
}
|
|
1218
|
+
infer(node.argument, scope, c, output);
|
|
1219
|
+
},
|
|
1220
|
+
|
|
1221
|
+
ForInStatement: function(node, scope, c) {
|
|
1222
|
+
var source = infer(node.right, scope, c);
|
|
1223
|
+
if ((node.right.type == "Identifier" && node.right.name in scope.props) ||
|
|
1224
|
+
(node.right.type == "MemberExpression" && node.right.property.name == "prototype")) {
|
|
1225
|
+
maybeInstantiate(scope, 5);
|
|
1226
|
+
var varName;
|
|
1227
|
+
if (node.left.type == "Identifier") {
|
|
1228
|
+
varName = node.left.name;
|
|
1229
|
+
} else if (node.left.type == "VariableDeclaration") {
|
|
1230
|
+
varName = node.left.declarations[0].id.name;
|
|
1231
|
+
}
|
|
1232
|
+
if (varName && varName in scope.props)
|
|
1233
|
+
scope.getProp(varName).iteratesOver = source;
|
|
1234
|
+
}
|
|
1235
|
+
c(node.body, scope, "Statement");
|
|
1236
|
+
},
|
|
1237
|
+
|
|
1238
|
+
ScopeBody: function(node, scope, c) { c(node, node.scope || scope); }
|
|
1239
|
+
});
|
|
1240
|
+
|
|
1241
|
+
// PARSING
|
|
1242
|
+
|
|
1243
|
+
function runPasses(passes, pass) {
|
|
1244
|
+
var arr = passes && passes[pass];
|
|
1245
|
+
var args = Array.prototype.slice.call(arguments, 2);
|
|
1246
|
+
if (arr) for (var i = 0; i < arr.length; ++i) arr[i].apply(null, args);
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
var parse = exports.parse = function(text, passes, options) {
|
|
1250
|
+
var ast;
|
|
1251
|
+
try { ast = acorn.parse(text, options); }
|
|
1252
|
+
catch(e) { ast = acorn_loose.parse_dammit(text, options); }
|
|
1253
|
+
runPasses(passes, "postParse", ast, text);
|
|
1254
|
+
return ast;
|
|
1255
|
+
};
|
|
1256
|
+
|
|
1257
|
+
// ANALYSIS INTERFACE
|
|
1258
|
+
|
|
1259
|
+
exports.analyze = function(ast, name, scope, passes) {
|
|
1260
|
+
if (typeof ast == "string") ast = parse(ast);
|
|
1261
|
+
|
|
1262
|
+
if (!name) name = "file#" + cx.origins.length;
|
|
1263
|
+
exports.addOrigin(cx.curOrigin = name);
|
|
1264
|
+
|
|
1265
|
+
if (!scope) scope = cx.topScope;
|
|
1266
|
+
walk.recursive(ast, scope, null, scopeGatherer);
|
|
1267
|
+
runPasses(passes, "preInfer", ast, scope);
|
|
1268
|
+
walk.recursive(ast, scope, null, inferWrapper);
|
|
1269
|
+
runPasses(passes, "postInfer", ast, scope);
|
|
1270
|
+
|
|
1271
|
+
cx.curOrigin = null;
|
|
1272
|
+
};
|
|
1273
|
+
|
|
1274
|
+
// PURGING
|
|
1275
|
+
|
|
1276
|
+
exports.purge = function(origins, start, end) {
|
|
1277
|
+
var test = makePredicate(origins, start, end);
|
|
1278
|
+
++cx.purgeGen;
|
|
1279
|
+
cx.topScope.purge(test);
|
|
1280
|
+
for (var prop in cx.props) {
|
|
1281
|
+
var list = cx.props[prop];
|
|
1282
|
+
for (var i = 0; i < list.length; ++i) {
|
|
1283
|
+
var obj = list[i], av = obj.props[prop];
|
|
1284
|
+
if (!av || test(av, av.originNode)) list.splice(i--, 1);
|
|
1285
|
+
}
|
|
1286
|
+
if (!list.length) delete cx.props[prop];
|
|
1287
|
+
}
|
|
1288
|
+
};
|
|
1289
|
+
|
|
1290
|
+
function makePredicate(origins, start, end) {
|
|
1291
|
+
var arr = Array.isArray(origins);
|
|
1292
|
+
if (arr && origins.length == 1) { origins = origins[0]; arr = false; }
|
|
1293
|
+
if (arr) {
|
|
1294
|
+
if (end == null) return function(n) { return origins.indexOf(n.origin) > -1; };
|
|
1295
|
+
return function(n, pos) { return pos && pos.start >= start && pos.end <= end && origins.indexOf(n.origin) > -1; };
|
|
1296
|
+
} else {
|
|
1297
|
+
if (end == null) return function(n) { return n.origin == origins; };
|
|
1298
|
+
return function(n, pos) { return pos && pos.start >= start && pos.end <= end && n.origin == origins; };
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1301
|
+
|
|
1302
|
+
AVal.prototype.purge = function(test) {
|
|
1303
|
+
if (this.purgeGen == cx.purgeGen) return;
|
|
1304
|
+
this.purgeGen = cx.purgeGen;
|
|
1305
|
+
for (var i = 0; i < this.types.length; ++i) {
|
|
1306
|
+
var type = this.types[i];
|
|
1307
|
+
if (test(type, type.originNode))
|
|
1308
|
+
this.types.splice(i--, 1);
|
|
1309
|
+
else
|
|
1310
|
+
type.purge(test);
|
|
1311
|
+
}
|
|
1312
|
+
if (this.forward) for (var i = 0; i < this.forward.length; ++i) {
|
|
1313
|
+
var f = this.forward[i];
|
|
1314
|
+
if (test(f)) {
|
|
1315
|
+
this.forward.splice(i--, 1);
|
|
1316
|
+
if (this.props) this.props = null;
|
|
1317
|
+
} else if (f.purge) {
|
|
1318
|
+
f.purge(test);
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
};
|
|
1322
|
+
ANull.purge = function() {};
|
|
1323
|
+
Obj.prototype.purge = function(test) {
|
|
1324
|
+
if (this.purgeGen == cx.purgeGen) return true;
|
|
1325
|
+
this.purgeGen = cx.purgeGen;
|
|
1326
|
+
for (var p in this.props) {
|
|
1327
|
+
var av = this.props[p];
|
|
1328
|
+
if (test(av, av.originNode))
|
|
1329
|
+
this.removeProp(p);
|
|
1330
|
+
av.purge(test);
|
|
1331
|
+
}
|
|
1332
|
+
};
|
|
1333
|
+
Fn.prototype.purge = function(test) {
|
|
1334
|
+
if (Obj.prototype.purge.call(this, test)) return;
|
|
1335
|
+
this.self.purge(test);
|
|
1336
|
+
this.retval.purge(test);
|
|
1337
|
+
for (var i = 0; i < this.args.length; ++i) this.args[i].purge(test);
|
|
1338
|
+
};
|
|
1339
|
+
|
|
1340
|
+
// EXPRESSION TYPE DETERMINATION
|
|
1341
|
+
|
|
1342
|
+
function findByPropertyName(name) {
|
|
1343
|
+
guessing = true;
|
|
1344
|
+
var found = objsWithProp(name);
|
|
1345
|
+
if (found) for (var i = 0; i < found.length; ++i) {
|
|
1346
|
+
var val = found[i].getProp(name);
|
|
1347
|
+
if (!val.isEmpty()) return val;
|
|
1348
|
+
}
|
|
1349
|
+
return ANull;
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
var typeFinder = {
|
|
1353
|
+
ArrayExpression: function(node, scope) {
|
|
1354
|
+
var eltval = new AVal;
|
|
1355
|
+
for (var i = 0; i < node.elements.length; ++i) {
|
|
1356
|
+
var elt = node.elements[i];
|
|
1357
|
+
if (elt) findType(elt, scope).propagate(eltval);
|
|
1358
|
+
}
|
|
1359
|
+
return new Arr(eltval);
|
|
1360
|
+
},
|
|
1361
|
+
ObjectExpression: function(node) {
|
|
1362
|
+
return node.objType;
|
|
1363
|
+
},
|
|
1364
|
+
FunctionExpression: function(node) {
|
|
1365
|
+
return node.body.scope.fnType;
|
|
1366
|
+
},
|
|
1367
|
+
SequenceExpression: function(node, scope) {
|
|
1368
|
+
return findType(node.expressions[node.expressions.length-1], scope);
|
|
1369
|
+
},
|
|
1370
|
+
UnaryExpression: function(node) {
|
|
1371
|
+
return unopResultType(node.operator);
|
|
1372
|
+
},
|
|
1373
|
+
UpdateExpression: function() {
|
|
1374
|
+
return cx.num;
|
|
1375
|
+
},
|
|
1376
|
+
BinaryExpression: function(node, scope) {
|
|
1377
|
+
if (binopIsBoolean(node.operator)) return cx.bool;
|
|
1378
|
+
if (node.operator == "+") {
|
|
1379
|
+
var lhs = findType(node.left, scope);
|
|
1380
|
+
var rhs = findType(node.right, scope);
|
|
1381
|
+
if (lhs.hasType(cx.str) || rhs.hasType(cx.str)) return cx.str;
|
|
1382
|
+
}
|
|
1383
|
+
return cx.num;
|
|
1384
|
+
},
|
|
1385
|
+
AssignmentExpression: function(node, scope) {
|
|
1386
|
+
return findType(node.right, scope);
|
|
1387
|
+
},
|
|
1388
|
+
LogicalExpression: function(node, scope) {
|
|
1389
|
+
var lhs = findType(node.left, scope);
|
|
1390
|
+
return lhs.isEmpty() ? findType(node.right, scope) : lhs;
|
|
1391
|
+
},
|
|
1392
|
+
ConditionalExpression: function(node, scope) {
|
|
1393
|
+
var lhs = findType(node.consequent, scope);
|
|
1394
|
+
return lhs.isEmpty() ? findType(node.alternate, scope) : lhs;
|
|
1395
|
+
},
|
|
1396
|
+
NewExpression: function(node, scope) {
|
|
1397
|
+
var f = findType(node.callee, scope).getFunctionType();
|
|
1398
|
+
var proto = f && f.getProp("prototype").getObjType();
|
|
1399
|
+
if (!proto) return ANull;
|
|
1400
|
+
return getInstance(proto, f);
|
|
1401
|
+
},
|
|
1402
|
+
CallExpression: function(node, scope) {
|
|
1403
|
+
var f = findType(node.callee, scope).getFunctionType();
|
|
1404
|
+
if (!f) return ANull;
|
|
1405
|
+
if (f.computeRet) {
|
|
1406
|
+
for (var i = 0, args = []; i < node.arguments.length; ++i)
|
|
1407
|
+
args.push(findType(node.arguments[i], scope));
|
|
1408
|
+
var self = ANull;
|
|
1409
|
+
if (node.callee.type == "MemberExpression")
|
|
1410
|
+
self = findType(node.callee.object, scope);
|
|
1411
|
+
return f.computeRet(self, args, node.arguments);
|
|
1412
|
+
} else {
|
|
1413
|
+
return f.retval;
|
|
1414
|
+
}
|
|
1415
|
+
},
|
|
1416
|
+
MemberExpression: function(node, scope) {
|
|
1417
|
+
var propN = propName(node, scope), obj = findType(node.object, scope).getType();
|
|
1418
|
+
if (obj) return obj.getProp(propN);
|
|
1419
|
+
if (propN == "<i>") return ANull;
|
|
1420
|
+
return findByPropertyName(propN);
|
|
1421
|
+
},
|
|
1422
|
+
Identifier: function(node, scope) {
|
|
1423
|
+
return scope.hasProp(node.name) || ANull;
|
|
1424
|
+
},
|
|
1425
|
+
ThisExpression: function(_node, scope) {
|
|
1426
|
+
return scope.fnType ? scope.fnType.self : cx.topScope;
|
|
1427
|
+
},
|
|
1428
|
+
Literal: function(node) {
|
|
1429
|
+
return literalType(node);
|
|
1430
|
+
}
|
|
1431
|
+
};
|
|
1432
|
+
|
|
1433
|
+
function findType(node, scope) {
|
|
1434
|
+
return typeFinder[node.type](node, scope);
|
|
1435
|
+
}
|
|
1436
|
+
|
|
1437
|
+
var searchVisitor = exports.searchVisitor = walk.make({
|
|
1438
|
+
Function: function(node, _st, c) {
|
|
1439
|
+
var scope = node.body.scope;
|
|
1440
|
+
if (node.id) c(node.id, scope);
|
|
1441
|
+
for (var i = 0; i < node.params.length; ++i)
|
|
1442
|
+
c(node.params[i], scope);
|
|
1443
|
+
c(node.body, scope, "ScopeBody");
|
|
1444
|
+
},
|
|
1445
|
+
TryStatement: function(node, st, c) {
|
|
1446
|
+
if (node.handler)
|
|
1447
|
+
c(node.handler.param, st);
|
|
1448
|
+
walk.base.TryStatement(node, st, c);
|
|
1449
|
+
},
|
|
1450
|
+
VariableDeclaration: function(node, st, c) {
|
|
1451
|
+
for (var i = 0; i < node.declarations.length; ++i) {
|
|
1452
|
+
var decl = node.declarations[i];
|
|
1453
|
+
c(decl.id, st);
|
|
1454
|
+
if (decl.init) c(decl.init, st, "Expression");
|
|
1455
|
+
}
|
|
1456
|
+
}
|
|
1457
|
+
});
|
|
1458
|
+
exports.fullVisitor = walk.make({
|
|
1459
|
+
MemberExpression: function(node, st, c) {
|
|
1460
|
+
c(node.object, st, "Expression");
|
|
1461
|
+
c(node.property, st, node.computed ? "Expression" : null);
|
|
1462
|
+
},
|
|
1463
|
+
ObjectExpression: function(node, st, c) {
|
|
1464
|
+
for (var i = 0; i < node.properties.length; ++i) {
|
|
1465
|
+
c(node.properties[i].value, st, "Expression");
|
|
1466
|
+
c(node.properties[i].key, st);
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
}, searchVisitor);
|
|
1470
|
+
|
|
1471
|
+
exports.findExpressionAt = function(ast, start, end, defaultScope, filter) {
|
|
1472
|
+
var test = filter || function(_t, node) {
|
|
1473
|
+
if (node.type == "Identifier" && node.name == "✖") return false;
|
|
1474
|
+
return typeFinder.hasOwnProperty(node.type);
|
|
1475
|
+
};
|
|
1476
|
+
return walk.findNodeAt(ast, start, end, test, searchVisitor, defaultScope || cx.topScope);
|
|
1477
|
+
};
|
|
1478
|
+
|
|
1479
|
+
exports.findExpressionAround = function(ast, start, end, defaultScope, filter) {
|
|
1480
|
+
var test = filter || function(_t, node) {
|
|
1481
|
+
if (start != null && node.start > start) return false;
|
|
1482
|
+
if (node.type == "Identifier" && node.name == "✖") return false;
|
|
1483
|
+
return typeFinder.hasOwnProperty(node.type);
|
|
1484
|
+
};
|
|
1485
|
+
return walk.findNodeAround(ast, end, test, searchVisitor, defaultScope || cx.topScope);
|
|
1486
|
+
};
|
|
1487
|
+
|
|
1488
|
+
exports.expressionType = function(found) {
|
|
1489
|
+
return findType(found.node, found.state);
|
|
1490
|
+
};
|
|
1491
|
+
|
|
1492
|
+
// Finding the expected type of something, from context
|
|
1493
|
+
|
|
1494
|
+
exports.parentNode = function(child, ast) {
|
|
1495
|
+
var stack = [];
|
|
1496
|
+
function c(node, st, override) {
|
|
1497
|
+
if (node.start <= child.start && node.end >= child.end) {
|
|
1498
|
+
var top = stack[stack.length - 1];
|
|
1499
|
+
if (node == child) throw {found: top};
|
|
1500
|
+
if (top != node) stack.push(node);
|
|
1501
|
+
walk.base[override || node.type](node, st, c);
|
|
1502
|
+
if (top != node) stack.pop();
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
try {
|
|
1506
|
+
c(ast, null);
|
|
1507
|
+
} catch (e) {
|
|
1508
|
+
if (e.found) return e.found;
|
|
1509
|
+
throw e;
|
|
1510
|
+
}
|
|
1511
|
+
};
|
|
1512
|
+
|
|
1513
|
+
var findTypeFromContext = {
|
|
1514
|
+
ArrayExpression: function(parent, _, get) { return get(parent, true).getProp("<i>"); },
|
|
1515
|
+
ObjectExpression: function(parent, node, get) {
|
|
1516
|
+
for (var i = 0; i < parent.properties.length; ++i) {
|
|
1517
|
+
var prop = node.properties[i];
|
|
1518
|
+
if (prop.value == node)
|
|
1519
|
+
return get(parent, true).getProp(prop.key.name);
|
|
1520
|
+
}
|
|
1521
|
+
},
|
|
1522
|
+
UnaryExpression: function(parent) { return unopResultType(parent.operator); },
|
|
1523
|
+
UpdateExpression: function() { return cx.num; },
|
|
1524
|
+
BinaryExpression: function(parent) { return binopIsBoolean(parent.operator) ? cx.bool : cx.num; },
|
|
1525
|
+
AssignmentExpression: function(parent, _, get) { return get(parent.left); },
|
|
1526
|
+
LogicalExpression: function(parent, _, get) { return get(parent, true); },
|
|
1527
|
+
ConditionalExpression: function(parent, node, get) {
|
|
1528
|
+
if (parent.consequent == node || parent.alternate == node) return get(parent, true);
|
|
1529
|
+
},
|
|
1530
|
+
NewExpression: function(parent, node, get) {
|
|
1531
|
+
return this.CallExpression(parent, node, get);
|
|
1532
|
+
},
|
|
1533
|
+
CallExpression: function(parent, node, get) {
|
|
1534
|
+
for (var i = 0; i < parent.arguments.length; i++) {
|
|
1535
|
+
var arg = parent.arguments[i];
|
|
1536
|
+
if (arg == node) {
|
|
1537
|
+
var calleeType = get(parent.callee).getFunctionType();
|
|
1538
|
+
if (calleeType instanceof Fn)
|
|
1539
|
+
return calleeType.args[i];
|
|
1540
|
+
break;
|
|
1541
|
+
}
|
|
1542
|
+
}
|
|
1543
|
+
},
|
|
1544
|
+
ReturnStatement: function(_parent, node, get) {
|
|
1545
|
+
var fnNode = walk.findNodeAround(node.sourceFile.ast, node.start, "Function");
|
|
1546
|
+
if (fnNode) {
|
|
1547
|
+
var fnType = fnNode.node.type == "FunctionExpression"
|
|
1548
|
+
? get(fnNode.node, true).getFunctionType()
|
|
1549
|
+
: fnNode.node.body.scope.fnType;
|
|
1550
|
+
if (fnType) return fnType.retval.getType();
|
|
1551
|
+
}
|
|
1552
|
+
},
|
|
1553
|
+
VariableDeclaration: function(parent, node, get) {
|
|
1554
|
+
for (var i = 0; i < parent.declarations.length; i++) {
|
|
1555
|
+
var decl = parent.declarations[i];
|
|
1556
|
+
if (decl.init == node) return get(decl.id);
|
|
1557
|
+
}
|
|
1558
|
+
}
|
|
1559
|
+
};
|
|
1560
|
+
|
|
1561
|
+
exports.typeFromContext = function(ast, found) {
|
|
1562
|
+
var parent = exports.parentNode(found.node, ast);
|
|
1563
|
+
var type = null;
|
|
1564
|
+
if (findTypeFromContext.hasOwnProperty(parent.type)) {
|
|
1565
|
+
type = findTypeFromContext[parent.type](parent, found.node, function(node, fromContext) {
|
|
1566
|
+
var obj = {node: node, state: found.state};
|
|
1567
|
+
var tp = fromContext ? exports.typeFromContext(ast, obj) : exports.expressionType(obj);
|
|
1568
|
+
return tp || ANull;
|
|
1569
|
+
});
|
|
1570
|
+
}
|
|
1571
|
+
return type || exports.expressionType(found);
|
|
1572
|
+
};
|
|
1573
|
+
|
|
1574
|
+
// Flag used to indicate that some wild guessing was used to produce
|
|
1575
|
+
// a type or set of completions.
|
|
1576
|
+
var guessing = false;
|
|
1577
|
+
|
|
1578
|
+
exports.resetGuessing = function(val) { guessing = val; };
|
|
1579
|
+
exports.didGuess = function() { return guessing; };
|
|
1580
|
+
|
|
1581
|
+
exports.forAllPropertiesOf = function(type, f) {
|
|
1582
|
+
type.gatherProperties(f, 0);
|
|
1583
|
+
};
|
|
1584
|
+
|
|
1585
|
+
var refFindWalker = walk.make({}, searchVisitor);
|
|
1586
|
+
|
|
1587
|
+
exports.findRefs = function(ast, baseScope, name, refScope, f) {
|
|
1588
|
+
refFindWalker.Identifier = function(node, scope) {
|
|
1589
|
+
if (node.name != name) return;
|
|
1590
|
+
for (var s = scope; s; s = s.prev) {
|
|
1591
|
+
if (s == refScope) f(node, scope);
|
|
1592
|
+
if (name in s.props) return;
|
|
1593
|
+
}
|
|
1594
|
+
};
|
|
1595
|
+
walk.recursive(ast, baseScope, null, refFindWalker);
|
|
1596
|
+
};
|
|
1597
|
+
|
|
1598
|
+
var simpleWalker = walk.make({
|
|
1599
|
+
Function: function(node, _st, c) { c(node.body, node.body.scope, "ScopeBody"); }
|
|
1600
|
+
});
|
|
1601
|
+
|
|
1602
|
+
exports.findPropRefs = function(ast, scope, objType, propName, f) {
|
|
1603
|
+
walk.simple(ast, {
|
|
1604
|
+
MemberExpression: function(node, scope) {
|
|
1605
|
+
if (node.computed || node.property.name != propName) return;
|
|
1606
|
+
if (findType(node.object, scope).getType() == objType) f(node.property);
|
|
1607
|
+
},
|
|
1608
|
+
ObjectExpression: function(node, scope) {
|
|
1609
|
+
if (findType(node, scope).getType() != objType) return;
|
|
1610
|
+
for (var i = 0; i < node.properties.length; ++i)
|
|
1611
|
+
if (node.properties[i].key.name == propName) f(node.properties[i].key);
|
|
1612
|
+
}
|
|
1613
|
+
}, simpleWalker, scope);
|
|
1614
|
+
};
|
|
1615
|
+
|
|
1616
|
+
// LOCAL-VARIABLE QUERIES
|
|
1617
|
+
|
|
1618
|
+
var scopeAt = exports.scopeAt = function(ast, pos, defaultScope) {
|
|
1619
|
+
var found = walk.findNodeAround(ast, pos, function(tp, node) {
|
|
1620
|
+
return tp == "ScopeBody" && node.scope;
|
|
1621
|
+
});
|
|
1622
|
+
if (found) return found.node.scope;
|
|
1623
|
+
else return defaultScope || cx.topScope;
|
|
1624
|
+
};
|
|
1625
|
+
|
|
1626
|
+
exports.forAllLocalsAt = function(ast, pos, defaultScope, f) {
|
|
1627
|
+
var scope = scopeAt(ast, pos, defaultScope);
|
|
1628
|
+
scope.gatherProperties(f, 0);
|
|
1629
|
+
};
|
|
1630
|
+
|
|
1631
|
+
// INIT DEF MODULE
|
|
1632
|
+
|
|
1633
|
+
// Delayed initialization because of cyclic dependencies.
|
|
1634
|
+
def = exports.def = def.init({}, exports);
|
|
1635
|
+
});
|