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

Side by Side Diff: frog/member_set.dart

Issue 9270048: Lots of frog cleanup (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 8 years, 11 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 String jsname;
9 final bool isVar;
10
11 bool _treatAsField;
12 Type _returnTypeForGet;
13 bool _preparedForSet = false;
14 List<InvokeKey> _invokes;
15
16 MemberSet(Member member, [bool isVar=false]):
17 name = member.name, members = [member], jsname = member.jsname,
18 isVar = isVar;
19
20 toString() => '$name:${members.length}';
21
22 void add(Member member) {
23 // Only methods on classes "really exist" - so warn if we add others?
24 members.add(member);
25 }
26
27 // TODO(jimhug): Better way to check for operator.
28 bool get isOperator() => members[0].isOperator;
29
30 Value _makeError(Node node, Value target, String action) {
31 if (!target.type.isVar) {
32 world.warning('could not find applicable $action for "$name"', node.span);
33 }
34 return new Value(world.varType,
35 '${target.code}.$jsname() /*no applicable $action*/', node.span);
36 }
37
38 bool get treatAsField() {
39 if (_treatAsField == null) {
40 _treatAsField = !isVar && members.every((m) => m.isField);
41 }
42 return _treatAsField;
43 }
44
45 static Type unionTypes(Type t1, Type t2) {
46 if (t1 == null) return t2;
47 if (t2 == null) return t1;
48 return Type.union(t1, t2);
49 }
50
51 /**
52 * This needs to generate one of the following:
53 * - target.name
54 * - target.get$name()
55 * - target.noSuchMethod(...)
56 *
57 * Can be treated as a field only if this is a properly resolved reference
58 * and all resolve members are fields.
59 */
60 Value _get(CallingContext context, Node node, Value target) {
61 if (members.length == 1 && !isVar) {
62 return members[0]._get(context, node, target);
63 }
64
65 if (_returnTypeForGet == null) {
66 for (var member in members) {
67 if (!member.canGet) continue;
68 if (!treatAsField) member.providePropertySyntax();
69 // TODO(jimhug): Need to make target less specific...
70 var r = member._get(context, node, target);
71 _returnTypeForGet = unionTypes(_returnTypeForGet, r.type);
72 }
73 if (_returnTypeForGet == null) {
74 world.error('no valid getters for "$name"', node.span);
75 }
76 }
77
78 if (_treatAsField) {
79 return new Value(_returnTypeForGet, '${target.code}.$jsname',
80 node.span);
81 } else {
82 return new Value(_returnTypeForGet, '${target.code}.get\$$jsname()',
83 node.span);
84 }
85 }
86
87 // TODO(jimhug): Return value of this method is unclear.
88 Value _set(CallingContext context, Node node, Value target, Value value) {
89 // If this is the global MemberSet from world, always bind dynamically.
90 // Note: we need this for proper noSuchMethod and REPL behavior.
91 if (members.length == 1 && !isVar) {
92 return members[0]._set(context, node, target, value);
93 }
94
95 if (!_preparedForSet) {
96 _preparedForSet = true;
97
98 for (var member in members) {
99 if (!member.canSet) continue;
100 if (!treatAsField) member.providePropertySyntax();
101 // !!! Need more generic args to this call below.
102 var r = member._set(context, node, target, value);
103 }
104 }
105
106 if (treatAsField) {
107 return new Value(value.type,
108 '${target.code}.$jsname = ${value.code}', node.span);
109 } else {
110 return new Value(value.type,
111 '${target.code}.set\$$jsname(${value.code})', node.span);
112 }
113 }
114
115 Value invoke(CallingContext context, Node node, Value target,
116 Arguments args) {
117 if (members.length == 1 && !isVar) {
118 return members[0].invoke(context, node, target, args);
119 }
120
121 var invokeKey = null;
122 if (_invokes == null) {
123 _invokes = [];
124 invokeKey = null;
125 } else {
126 for (var ik in _invokes) {
127 if (ik.matches(args)) {
128 invokeKey = ik;
129 break;
130 }
131 }
132 }
133 if (invokeKey == null) {
134 invokeKey = new InvokeKey(args);
135 _invokes.add(invokeKey);
136 invokeKey.addMembers(members, context, target, args);
137 }
138
139 // TODO(jimhug): isOperator test is too lenient - misses opt chances
140 if (invokeKey.needsVarCall || isOperator) {
141 if (name == ':call') {
142 return target._varCall(context, node, args);
143 } else if (isOperator) {
144 // TODO(jmesserly): make operators less special.
145 return invokeSpecial(target, args, invokeKey.returnType);
146 } else {
147 return invokeOnVar(context, node, target, args);
148 }
149 } else {
150 var code = '${target.code}.${jsname}(${args.getCode()})';
151 return new Value(invokeKey.returnType, code, node.span);
152 }
153 }
154
155 Value invokeSpecial(Value target, Arguments args, Type returnType) {
156 assert(name.startsWith(':'));
157 assert(!args.hasNames);
158 // TODO(jimhug): We need to do this a little bit more like get and set on
159 // properties. We should check the set of members for something
160 // like "requiresNativeIndexer" and "requiresDartIndexer" to
161 // decide on a strategy.
162
163 var argsString = args.getCode();
164 // Most operator calls need to be emitted as function calls, so we don't
165 // box numbers accidentally. Indexing is the exception.
166 if (name == ':index' || name == ':setindex') {
167 // TODO(jimhug): should not need this test both here and in invoke
168 if (name == ':index') {
169 world.gen.corejs.useIndex = true;
170 } else if (name == ':setindex') {
171 world.gen.corejs.useSetIndex = true;
172 }
173 return new Value(returnType, '${target.code}.$jsname($argsString)',
174 target.span);
175 } else {
176 if (argsString.length > 0) argsString = ', $argsString';
177 world.gen.corejs.useOperator(name);
178 return new Value(returnType, '$jsname(${target.code}$argsString)',
179 target.span);
180 }
181 }
182
183 Value invokeOnVar(CallingContext context, Node node, Value target,
184 Arguments args) {
185 context.counters.dynamicMethodCalls++;
186
187 var member = getVarMember(context, node, args);
188 return member.invoke(context, node, target, args);
189 }
190
191 dumpAllMembers() {
192 for (var member in members) {
193 world.warning('hard-multi $name on ${member.declaringType.name}',
194 member.span);
195 }
196 }
197
198 VarMember getVarMember(CallingContext context, Node node, Arguments args) {
199 if (world.objectType.varStubs == null) {
200 world.objectType.varStubs = {};
201 }
202
203 var stubName = _getCallStubName(name, args);
204 var stub = world.objectType.varStubs[stubName];
205 if (stub == null) {
206 // Ensure that we're making stub with all possible members of this name.
207 // We need this canonicalization step because only one VarMemberSet can
208 // live on Object.prototype
209 // TODO(jmesserly): this is ugly--we're throwing away type information!
210 // The right solution is twofold:
211 // 1. put stubs on a more precise type when possible
212 // 2. merge VarMemberSets together if necessary
213 final mset = context.findMembers(name).members;
214
215 final targets = mset.filter((m) => m.canInvoke(context, args));
216 stub = new VarMethodSet(name, stubName, targets, args,
217 _foldTypes(targets));
218 world.objectType.varStubs[stubName] = stub;
219 }
220 return stub;
221 }
222
223 Type _foldTypes(List<Member> targets) =>
224 reduce(map(targets, (t) => t.returnType), Type.union, world.varType);
225 }
226
227
228 class InvokeKey {
229 int bareArgs;
230 List<String> namedArgs;
231 Type returnType;
232 bool needsVarCall = false;
233
234 InvokeKey(Arguments args) {
235 bareArgs = args.bareCount;
236 if (bareArgs != args.length) {
237 namedArgs = args.getNames();
238 }
239 }
240
241 bool matches(Arguments args) {
242 if (namedArgs == null) {
243 if (bareArgs != args.length) return false;
244 } else {
245 if (bareArgs + namedArgs.length != args.length) return false;
246 }
247 if (bareArgs != args.bareCount) return false;
248
249 if (namedArgs == null) return true;
250
251 for (int i = 0; i < namedArgs.length; i++) {
252 if (namedArgs[i] != args.getName(bareArgs + i)) return false;
253 }
254 return true;
255 }
256
257 void addMembers(List<Member> members, CallingContext context, Value target,
258 Arguments args) {
259 // TODO(jimhug): In checked mode include parameter types to determine
260 // need for varCall.
261 for (var member in members) {
262 // check that this is a "perfect" match - or require a var call
263 // TODO(jimhug): Add support of "perfect matches" even with names
264 if (!(member.parameters.length == bareArgs && namedArgs == null)) {
265 needsVarCall = true;
266 }
267 // TODO(jimhug): Should create a less specific version of args.
268 if (member.canInvoke(context, args)) {
269 if (member.isMethod) {
270 returnType = MemberSet.unionTypes(returnType, member.returnType);
271 member.declaringType.genMethod(member);
272 } else {
273 needsVarCall = true;
274 returnType = world.varType;
275 }
276 }
277 }
278 if (returnType == null) {
279 // TODO(jimhug): Warning here for no match anywhere in the world?
280 returnType = world.varType;
281 }
282 }
283 }
OLDNEW
« frog/gen.dart ('K') | « frog/member.dart ('k') | frog/method_data.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698