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

Side by Side Diff: pkg/compiler/lib/src/js_emitter/parameter_stub_generator.dart

Issue 887853004: dart2js: Move parameterStub generation to parameter_stub_generator and add parameter stubs to model. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Rebased. Created 5 years, 10 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) 2015, 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 part of dart2js.js_emitter;
6
7 class ParameterStubGenerator {
8 final Namer namer;
9 final Compiler compiler;
10 final JavaScriptBackend backend;
11
12 ParameterStubGenerator(this.compiler, this.namer, this.backend);
13
14 Emitter get emitter => backend.emitter.emitter;
15 CodeEmitterTask get emitterTask => backend.emitter;
16
17 bool needsSuperGetter(FunctionElement element) =>
18 compiler.codegenWorld.methodsNeedingSuperGetter.contains(element);
19
20 /**
21 * Generate stubs to handle invocation of methods with optional
22 * arguments.
23 *
24 * A method like [: foo([x]) :] may be invoked by the following
25 * calls: [: foo(), foo(1), foo(x: 1) :]. See the sources of this
26 * function for detailed examples.
27 */
28 jsAst.Expression generateParameterStub(FunctionElement member,
29 Selector selector) {
30 FunctionSignature parameters = member.functionSignature;
31 int positionalArgumentCount = selector.positionalArgumentCount;
32 if (positionalArgumentCount == parameters.parameterCount) {
33 assert(selector.namedArgumentCount == 0);
34 return null;
35 }
36 if (parameters.optionalParametersAreNamed
37 && selector.namedArgumentCount == parameters.optionalParameterCount) {
38 // If the selector has the same number of named arguments as the element,
39 // we don't need to add a stub. The call site will hit the method
40 // directly.
41 return null;
42 }
43 JavaScriptConstantCompiler handler = backend.constants;
44 List<String> names = selector.getOrderedNamedArguments();
45
46 String invocationName = namer.invocationName(selector);
47
48 bool isInterceptedMethod = backend.isInterceptedMethod(member);
49
50 // If the method is intercepted, we need to also pass the actual receiver.
51 int extraArgumentCount = isInterceptedMethod ? 1 : 0;
52 // Use '$receiver' to avoid clashes with other parameter names. Using
53 // '$receiver' works because [:namer.safeName:] used for getting parameter
54 // names never returns a name beginning with a single '$'.
55 String receiverArgumentName = r'$receiver';
56
57 // The parameters that this stub takes.
58 List<jsAst.Parameter> parametersBuffer =
59 new List<jsAst.Parameter>(selector.argumentCount + extraArgumentCount);
60 // The arguments that will be passed to the real method.
61 List<jsAst.Expression> argumentsBuffer =
62 new List<jsAst.Expression>(
63 parameters.parameterCount + extraArgumentCount);
64
65 int count = 0;
66 if (isInterceptedMethod) {
67 count++;
68 parametersBuffer[0] = new jsAst.Parameter(receiverArgumentName);
69 argumentsBuffer[0] = js('#', receiverArgumentName);
70 }
71
72 int optionalParameterStart = positionalArgumentCount + extraArgumentCount;
73 // Includes extra receiver argument when using interceptor convention
74 int indexOfLastOptionalArgumentInParameters = optionalParameterStart - 1;
75
76 int parameterIndex = 0;
77 parameters.orderedForEachParameter((ParameterElement element) {
78 String jsName = backend.namer.safeName(element.name);
79 assert(jsName != receiverArgumentName);
80 if (count < optionalParameterStart) {
81 parametersBuffer[count] = new jsAst.Parameter(jsName);
82 argumentsBuffer[count] = js('#', jsName);
83 } else {
84 int index = names.indexOf(element.name);
85 if (index != -1) {
86 indexOfLastOptionalArgumentInParameters = count;
87 // The order of the named arguments is not the same as the
88 // one in the real method (which is in Dart source order).
89 argumentsBuffer[count] = js('#', jsName);
90 parametersBuffer[optionalParameterStart + index] =
91 new jsAst.Parameter(jsName);
92 } else {
93 ConstantExpression constant = handler.getConstantForVariable(element);
94 if (constant == null) {
95 argumentsBuffer[count] =
96 emitter.constantReference(new NullConstantValue());
97 } else {
98 ConstantValue value = constant.value;
99 if (!value.isNull) {
100 // If the value is the null constant, we should not pass it
101 // down to the native method.
102 indexOfLastOptionalArgumentInParameters = count;
103 }
104 argumentsBuffer[count] = emitter.constantReference(value);
105 }
106 }
107 }
108 count++;
109 });
110
111 var body; // List or jsAst.Statement.
112 if (member.hasFixedBackendName) {
113 body = emitterTask.nativeEmitter.generateParameterStubStatements(
114 member, isInterceptedMethod, invocationName,
115 parametersBuffer, argumentsBuffer,
116 indexOfLastOptionalArgumentInParameters);
117 } else if (member.isInstanceMember) {
118 if (needsSuperGetter(member)) {
119 ClassElement superClass = member.enclosingClass;
120 String methodName = namer.getNameOfInstanceMember(member);
121 // When redirecting, we must ensure that we don't end up in a subclass.
122 // We thus can't just invoke `this.foo$1.call(filledInArguments)`.
123 // Instead we need to call the statically resolved target.
124 // `<class>.prototype.bar$1.call(this, argument0, ...)`.
125 body = js.statement(
126 'return #.#.call(this, #);',
127 [backend.emitter.prototypeAccess(superClass,
128 hasBeenInstantiated: true),
129 methodName,
130 argumentsBuffer]);
131 } else {
132 body = js.statement(
133 'return this.#(#);',
134 [namer.getNameOfInstanceMember(member), argumentsBuffer]);
135 }
136 } else {
137 body = js.statement('return #(#)',
138 [emitter.staticFunctionAccess(member), argumentsBuffer]);
139 }
140
141 jsAst.Fun function = js('function(#) { #; }', [parametersBuffer, body]);
142
143 return function;
144 }
145
146 Map<Selector, jsAst.Expression> generateParameterStubs(FunctionElement member,
147 [bool canTearOff = false]) {
148 Map<Selector, jsAst.Expression> generatedStubs
149 = <Selector, jsAst.Expression>{};
150
151 if (member.enclosingElement.isClosure) {
152 ClosureClassElement cls = member.enclosingElement;
153 if (cls.supertype.element == backend.boundClosureClass) {
154 compiler.internalError(cls.methodElement, 'Bound closure1.');
155 }
156 if (cls.methodElement.isInstanceMember) {
157 compiler.internalError(cls.methodElement, 'Bound closure2.');
158 }
159 }
160
161 // We fill the lists depending on the selector. For example,
162 // take method foo:
163 // foo(a, b, {c, d});
164 //
165 // We may have multiple ways of calling foo:
166 // (1) foo(1, 2);
167 // (2) foo(1, 2, c: 3);
168 // (3) foo(1, 2, d: 4);
169 // (4) foo(1, 2, c: 3, d: 4);
170 // (5) foo(1, 2, d: 4, c: 3);
171 //
172 // What we generate at the call sites are:
173 // (1) foo$2(1, 2);
174 // (2) foo$3$c(1, 2, 3);
175 // (3) foo$3$d(1, 2, 4);
176 // (4) foo$4$c$d(1, 2, 3, 4);
177 // (5) foo$4$c$d(1, 2, 3, 4);
178 //
179 // The stubs we generate are (expressed in Dart):
180 // (1) foo$2(a, b) => foo$4$c$d(a, b, null, null)
181 // (2) foo$3$c(a, b, c) => foo$4$c$d(a, b, c, null);
182 // (3) foo$3$d(a, b, d) => foo$4$c$d(a, b, null, d);
183 // (4) No stub generated, call is direct.
184 // (5) No stub generated, call is direct.
185 //
186 // We need to pay attention if this stub is for a function that has been
187 // invoked from a subclass. Then we cannot just redirect, since that
188 // would invoke the methods of the subclass. We have to compile to:
189 // (1) foo$2(a, b) => MyClass.foo$4$c$d.call(this, a, b, null, null)
190 // (2) foo$3$c(a, b, c) => MyClass.foo$4$c$d(this, a, b, c, null);
191 // (3) foo$3$d(a, b, d) => MyClass.foo$4$c$d(this, a, b, null, d);
192
193 Set<Selector> selectors = member.isInstanceMember
194 ? compiler.codegenWorld.invokedNames[member.name]
195 : null; // No stubs needed for static methods.
196
197 /// Returns all closure call selectors renamed to match this member.
198 Set<Selector> callSelectorsAsNamed() {
199 if (!canTearOff) return null;
200 Set<Selector> callSelectors = compiler.codegenWorld.invokedNames[
201 namer.closureInvocationSelectorName];
202 if (callSelectors == null) return null;
203 return callSelectors.map((Selector callSelector) {
204 return new Selector.call(
205 member.name, member.library,
206 callSelector.argumentCount, callSelector.namedArguments);
207 }).toSet();
208 }
209 if (selectors == null) {
210 selectors = callSelectorsAsNamed();
211 if (selectors == null) return generatedStubs;
212 } else {
213 Set<Selector> callSelectors = callSelectorsAsNamed();
214 if (callSelectors != null) {
215 selectors = selectors.union(callSelectors);
216 }
217 }
218 Set<Selector> untypedSelectors = new Set<Selector>();
219 if (selectors != null) {
220 for (Selector selector in selectors) {
221 if (!selector.appliesUnnamed(member, compiler.world)) continue;
222 if (untypedSelectors.add(selector.asUntyped)) {
223 jsAst.Expression stub = generateParameterStub(member, selector);
224 if (stub != null) {
225 generatedStubs[selector] = stub;
226 }
227 }
228 }
229 }
230 if (canTearOff) {
231 selectors = compiler.codegenWorld.invokedNames[
232 namer.closureInvocationSelectorName];
233 if (selectors != null) {
234 for (Selector selector in selectors) {
235 selector = new Selector.call(
236 member.name, member.library,
237 selector.argumentCount, selector.namedArguments);
238 if (!selector.appliesUnnamed(member, compiler.world)) continue;
239 if (untypedSelectors.add(selector)) {
240 jsAst.Expression stub = generateParameterStub(member, selector);
241 if (stub != null) {
242 generatedStubs[selector] = stub;
243 }
244 }
245 }
246 }
247 }
248 return generatedStubs;
249 }
250 }
OLDNEW
« no previous file with comments | « pkg/compiler/lib/src/js_emitter/old_emitter/declarations.dart ('k') | pkg/compiler/lib/src/js_emitter/program_builder.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698