Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(344)

Side by Side Diff: lib/dartdoc/frog/member_set.dart

Issue 10696191: Frog removed from dartdoc. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 8 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4
5 class MemberSet {
6 final String name;
7 final List<Member> members;
8 final bool isVar;
9
10 bool _treatAsField;
11 Type _returnTypeForGet;
12 bool _preparedForSet = false;
13 List<InvokeKey> _invokes;
14
15 MemberSet(Member member, [bool isVar=false])
16 : name = member.name, members = [member], isVar = isVar;
17
18 toString() => '$name:${members.length}';
19
20 /**
21 * [jsname] should be called only when it is known that all the members have
22 * the same jsname. (The name safety pass can cause members of the same
23 * MemberSet to have different jsnames.)
24 */
25 String get jsname() => members[0].jsname;
26
27 void add(Member member) {
28 // Only methods on classes "really exist" - so warn if we add others?
29 members.add(member);
30 }
31
32 // TODO(jimhug): Better way to check for operator.
33 bool get isOperator() => members[0].isOperator;
34
35 bool get treatAsField() {
36 if (_treatAsField == null) {
37 // Must be all fields, all with the same jsname.
38 _treatAsField = !isVar &&
39 members.every((m) => m.isField && m.jsname == members[0].jsname);
40 }
41 return _treatAsField;
42 }
43
44 static Type unionTypes(Type t1, Type t2) {
45 if (t1 == null) return t2;
46 if (t2 == null) return t1;
47 return Type.union(t1, t2);
48 }
49
50 /**
51 * This needs to generate one of the following:
52 * - target.name
53 * - target.get$name()
54 * - target.noSuchMethod(...)
55 *
56 * Can be treated as a field only if this is a properly resolved reference
57 * and all resolve members are fields.
58 */
59 Value _get(CallingContext context, Node node, Value target) {
60 if (members.length == 1 && !isVar) {
61 return members[0]._get(context, node, target);
62 }
63
64 if (_returnTypeForGet == null) {
65 for (var member in members) {
66 if (!member.canGet) continue;
67 if (!treatAsField) member.provideGetter();
68 // TODO(jimhug): Need to make target less specific...
69 var r = member._get(context, node, target);
70 _returnTypeForGet = unionTypes(_returnTypeForGet, r.type);
71 }
72 if (_returnTypeForGet == null) {
73 world.error('no valid getters for "$name"', node.span);
74 }
75 }
76
77 if (_treatAsField) {
78 return new Value(_returnTypeForGet,
79 '${target.code}.$jsname', node.span);
80 } else {
81 return new Value(_returnTypeForGet,
82 '${target.code}.${members[0].jsnameOfGetter}()', node.span);
83 }
84 }
85
86 // TODO(jimhug): Return value of this method is unclear.
87 Value _set(CallingContext context, Node node, Value target, Value value) {
88 // If this is the global MemberSet from world, always bind dynamically.
89 // Note: we need this for proper noSuchMethod and REPL behavior.
90 if (members.length == 1 && !isVar) {
91 return members[0]._set(context, node, target, value);
92 }
93
94 if (!_preparedForSet) {
95 _preparedForSet = true;
96
97 for (var member in members) {
98 if (!member.canSet) continue;
99 if (!treatAsField) member.provideSetter();
100 // !!! Need more generic args to this call below.
101 var r = member._set(context, node, target, value);
102 }
103 }
104
105 if (treatAsField) {
106 return new Value(value.type,
107 '${target.code}.$jsname = ${value.code}', node.span);
108 } else {
109 return new Value(value.type,
110 '${target.code}.${members[0].jsnameOfSetter}(${value.code})', node.span) ;
111 }
112 }
113
114 Value invoke(CallingContext context, Node node, Value target,
115 Arguments args) {
116 if (members.length == 1 && !isVar) {
117 return members[0].invoke(context, node, target, args);
118 }
119
120 var invokeKey = null;
121 if (_invokes == null) {
122 _invokes = [];
123 invokeKey = null;
124 } else {
125 for (var ik in _invokes) {
126 if (ik.matches(args)) {
127 invokeKey = ik;
128 break;
129 }
130 }
131 }
132 if (invokeKey == null) {
133 invokeKey = new InvokeKey(args);
134 _invokes.add(invokeKey);
135 invokeKey.addMembers(members, context, target, args);
136 }
137
138 // TODO(jimhug): isOperator test is too lenient - misses opt chances
139 if (invokeKey.needsVarCall || isOperator) {
140 if (name == ':call') {
141 return target._varCall(context, node, args);
142 } else if (isOperator) {
143 // TODO(jmesserly): make operators less special.
144 return invokeSpecial(target, args, invokeKey.returnType);
145 } else {
146 return invokeOnVar(context, node, target, args);
147 }
148 } else {
149 var code = '${target.code}.${jsname}(${args.getCode()})';
150 return new Value(invokeKey.returnType, code, node.span);
151 }
152 }
153
154 Value invokeSpecial(Value target, Arguments args, Type returnType) {
155 assert(name.startsWith(':'));
156 assert(!args.hasNames);
157 // TODO(jimhug): We need to do this a little bit more like get and set on
158 // properties. We should check the set of members for something
159 // like "requiresNativeIndexer" and "requiresDartIndexer" to
160 // decide on a strategy.
161
162 var argsString = args.getCode();
163 // Most operator calls need to be emitted as function calls, so we don't
164 // box numbers accidentally. Indexing is the exception.
165 if (name == ':index' || name == ':setindex') {
166 // TODO(jimhug): should not need this test both here and in invoke
167 if (name == ':index') {
168 world.gen.corejs.useIndex = true;
169 } else if (name == ':setindex') {
170 world.gen.corejs.useSetIndex = true;
171 }
172 return new Value(returnType, '${target.code}.$jsname($argsString)',
173 target.span);
174 } else {
175 if (argsString.length > 0) argsString = ', $argsString';
176 world.gen.corejs.useOperator(name);
177 return new Value(returnType, '$jsname\$(${target.code}$argsString)',
178 target.span);
179 }
180 }
181
182 Value invokeOnVar(CallingContext context, Node node, Value target,
183 Arguments args) {
184 context.counters.dynamicMethodCalls++;
185
186 var member = getVarMember(context, node, args);
187 return member.invoke(context, node, target, args);
188 }
189
190 dumpAllMembers() {
191 for (var member in members) {
192 world.warning('hard-multi $name on ${member.declaringType.name}',
193 member.span);
194 }
195 }
196
197 VarMember getVarMember(CallingContext context, Node node, Arguments args) {
198 if (world.objectType.varStubs == null) {
199 world.objectType.varStubs = {};
200 }
201
202 var stubName = _getCallStubName(name, args);
203 var stub = world.objectType.varStubs[stubName];
204 if (stub == null) {
205 // Ensure that we're making stub with all possible members of this name.
206 // We need this canonicalization step because only one VarMemberSet can
207 // live on Object.prototype
208 // TODO(jmesserly): this is ugly--we're throwing away type information!
209 // The right solution is twofold:
210 // 1. put stubs on a more precise type when possible
211 // 2. merge VarMemberSets together if necessary
212 final mset = context.findMembers(name).members;
213
214 final targets = mset.filter((m) => m.canInvoke(context, args));
215 stub = new VarMethodSet(name, stubName, targets, args,
216 _foldTypes(targets));
217 world.objectType.varStubs[stubName] = stub;
218 }
219 return stub;
220 }
221
222 Type _foldTypes(List<Member> targets) =>
223 reduce(map(targets, (t) => t.returnType), Type.union, world.varType);
224 }
225
226
227 class InvokeKey {
228 int bareArgs;
229 List<String> namedArgs;
230 Type returnType;
231 bool needsVarCall = false;
232
233 InvokeKey(Arguments args) {
234 bareArgs = args.bareCount;
235 if (bareArgs != args.length) {
236 namedArgs = args.getNames();
237 }
238 }
239
240 bool matches(Arguments args) {
241 if (namedArgs == null) {
242 if (bareArgs != args.length) return false;
243 } else {
244 if (bareArgs + namedArgs.length != args.length) return false;
245 }
246 if (bareArgs != args.bareCount) return false;
247
248 if (namedArgs == null) return true;
249
250 for (int i = 0; i < namedArgs.length; i++) {
251 if (namedArgs[i] != args.getName(bareArgs + i)) return false;
252 }
253 return true;
254 }
255
256 void addMembers(List<Member> members, CallingContext context, Value target,
257 Arguments args) {
258 for (var member in members) {
259 // Check that this is a "perfect" match - or require a var call.
260 // TODO(jimhug): Add support of "perfect matches" even with names.
261 if (!(member.parameters.length == bareArgs && namedArgs == null)) {
262 // If we have named arguments or a mismatch in the number of
263 // formal and actual parameters, we go through a var call.
264 needsVarCall = true;
265 } else if (options.enableTypeChecks &&
266 member.isMethod &&
267 member.needsArgumentConversion(args)) {
268 // The member we're adding is a method that needs argument
269 // conversion, so we have to make it go through the var call
270 // path to get the correct type checks inserted.
271 needsVarCall = true;
272 } else if (member.jsname != members[0].jsname) {
273 // If the jsnames differ we need the var call since one of the stubs
274 // will change the name. Native methods can have different jsnames,
275 // e.g.
276 // foo() native 'bar';
277 needsVarCall = true;
278 } else if (member.library.isDomOrHtml) {
279 // TODO(jimhug): Egregious hack for isolates + DOM - see
280 // Value._maybeWrapFunction for more details.
281 for (var p in member.parameters) {
282 if (p.type.getCallMethod() != null) {
283 needsVarCall = true;
284 }
285 }
286 }
287
288 // TODO(jimhug): Should create a less specific version of args.
289 if (member.canInvoke(context, args)) {
290 if (member.isMethod) {
291 returnType = MemberSet.unionTypes(returnType, member.returnType);
292 member.declaringType.genMethod(member);
293 } else {
294 needsVarCall = true;
295 returnType = world.varType;
296 }
297 }
298 }
299 if (returnType == null) {
300 // TODO(jimhug): Warning here for no match anywhere in the world?
301 returnType = world.varType;
302 }
303 }
304 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698