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

Unified 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: Forgot to save container_builder Created 5 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 side-by-side diff with in-line comments
Download patch
Index: pkg/compiler/lib/src/js_emitter/parameter_stub_generator.dart
diff --git a/pkg/compiler/lib/src/js_emitter/parameter_stub_generator.dart b/pkg/compiler/lib/src/js_emitter/parameter_stub_generator.dart
new file mode 100644
index 0000000000000000000000000000000000000000..65339d406bea38b73c9d3f84b5e484cce523b087
--- /dev/null
+++ b/pkg/compiler/lib/src/js_emitter/parameter_stub_generator.dart
@@ -0,0 +1,250 @@
+// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
floitsch 2015/01/30 12:33:03 2015
zarah 2015/01/30 12:46:39 Done.
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+part of dart2js.js_emitter;
+
+class ParameterStubGenerator {
floitsch 2015/01/30 12:24:45 I think this class could have been merged into the
floitsch 2015/01/30 12:25:24 Please don't merge it now, since I'm going to reba
zarah 2015/01/30 12:46:39 I was also considering that. So I agree.
+ final Namer namer;
+ final Compiler compiler;
+ final JavaScriptBackend backend;
+
+ ParameterStubGenerator(this.compiler, this.namer, this.backend);
+
+ Emitter get emitter => backend.emitter.emitter;
+ CodeEmitterTask get emitterTask => backend.emitter;
+
+ bool needsSuperGetter(FunctionElement element) =>
+ compiler.codegenWorld.methodsNeedingSuperGetter.contains(element);
+
+ /**
+ * Generate stubs to handle invocation of methods with optional
+ * arguments.
+ *
+ * A method like [: foo([x]) :] may be invoked by the following
+ * calls: [: foo(), foo(1), foo(x: 1) :]. See the sources of this
+ * function for detailed examples.
+ */
+ jsAst.Expression generateParameterStub(FunctionElement member,
+ Selector selector) {
floitsch 2015/01/30 12:24:45 nit: indentation.
zarah 2015/01/30 12:46:39 Done.
+ FunctionSignature parameters = member.functionSignature;
+ int positionalArgumentCount = selector.positionalArgumentCount;
+ if (positionalArgumentCount == parameters.parameterCount) {
+ assert(selector.namedArgumentCount == 0);
+ return null;
+ }
+ if (parameters.optionalParametersAreNamed
+ && selector.namedArgumentCount == parameters.optionalParameterCount) {
+ // If the selector has the same number of named arguments as the element,
+ // we don't need to add a stub. The call site will hit the method
+ // directly.
+ return null;
+ }
+ JavaScriptConstantCompiler handler = backend.constants;
+ List<String> names = selector.getOrderedNamedArguments();
+
+ String invocationName = namer.invocationName(selector);
+
+ bool isInterceptedMethod = backend.isInterceptedMethod(member);
+
+ // If the method is intercepted, we need to also pass the actual receiver.
+ int extraArgumentCount = isInterceptedMethod ? 1 : 0;
+ // Use '$receiver' to avoid clashes with other parameter names. Using
+ // '$receiver' works because [:namer.safeName:] used for getting parameter
+ // names never returns a name beginning with a single '$'.
+ String receiverArgumentName = r'$receiver';
+
+ // The parameters that this stub takes.
+ List<jsAst.Parameter> parametersBuffer =
+ new List<jsAst.Parameter>(selector.argumentCount + extraArgumentCount);
+ // The arguments that will be passed to the real method.
+ List<jsAst.Expression> argumentsBuffer =
+ new List<jsAst.Expression>(
+ parameters.parameterCount + extraArgumentCount);
+
+ int count = 0;
+ if (isInterceptedMethod) {
+ count++;
+ parametersBuffer[0] = new jsAst.Parameter(receiverArgumentName);
+ argumentsBuffer[0] = js('#', receiverArgumentName);
+ }
+
+ int optionalParameterStart = positionalArgumentCount + extraArgumentCount;
+ // Includes extra receiver argument when using interceptor convention
+ int indexOfLastOptionalArgumentInParameters = optionalParameterStart - 1;
+
+ int parameterIndex = 0;
+ parameters.orderedForEachParameter((ParameterElement element) {
+ String jsName = backend.namer.safeName(element.name);
+ assert(jsName != receiverArgumentName);
+ if (count < optionalParameterStart) {
+ parametersBuffer[count] = new jsAst.Parameter(jsName);
+ argumentsBuffer[count] = js('#', jsName);
+ } else {
+ int index = names.indexOf(element.name);
+ if (index != -1) {
+ indexOfLastOptionalArgumentInParameters = count;
+ // The order of the named arguments is not the same as the
+ // one in the real method (which is in Dart source order).
+ argumentsBuffer[count] = js('#', jsName);
+ parametersBuffer[optionalParameterStart + index] =
+ new jsAst.Parameter(jsName);
+ } else {
+ ConstantExpression constant = handler.getConstantForVariable(element);
+ if (constant == null) {
+ argumentsBuffer[count] =
+ emitter.constantReference(new NullConstantValue());
+ } else {
+ ConstantValue value = constant.value;
+ if (!value.isNull) {
+ // If the value is the null constant, we should not pass it
+ // down to the native method.
+ indexOfLastOptionalArgumentInParameters = count;
+ }
+ argumentsBuffer[count] = emitter.constantReference(value);
+ }
+ }
+ }
+ count++;
+ });
+
+ var body; // List or jsAst.Statement.
+ if (member.hasFixedBackendName) {
+ body = emitterTask.nativeEmitter.generateParameterStubStatements(
+ member, isInterceptedMethod, invocationName,
+ parametersBuffer, argumentsBuffer,
+ indexOfLastOptionalArgumentInParameters);
+ } else if (member.isInstanceMember) {
+ if (needsSuperGetter(member)) {
+ ClassElement superClass = member.enclosingClass;
+ String methodName = namer.getNameOfInstanceMember(member);
+ // When redirecting, we must ensure that we don't end up in a subclass.
+ // We thus can't just invoke `this.foo$1.call(filledInArguments)`.
+ // Instead we need to call the statically resolved target.
+ // `<class>.prototype.bar$1.call(this, argument0, ...)`.
+ body = js.statement(
+ 'return #.#.call(this, #);',
+ [backend.emitter.prototypeAccess(superClass,
+ hasBeenInstantiated: true),
+ methodName,
+ argumentsBuffer]);
+ } else {
+ body = js.statement(
+ 'return this.#(#);',
+ [namer.getNameOfInstanceMember(member), argumentsBuffer]);
+ }
+ } else {
+ body = js.statement('return #(#)',
+ [emitter.staticFunctionAccess(member), argumentsBuffer]);
+ }
+
+ jsAst.Fun function = js('function(#) { #; }', [parametersBuffer, body]);
+
+ return function;
+ }
+
+ Map<Selector, jsAst.Expression> generateParameterStubs(FunctionElement member,
+ [bool canTearOff = false]) {
+ Map<Selector, jsAst.Expression> generatedStubs
+ = <Selector, jsAst.Expression>{};
+
+ if (member.enclosingElement.isClosure) {
+ ClosureClassElement cls = member.enclosingElement;
+ if (cls.supertype.element == backend.boundClosureClass) {
+ compiler.internalError(cls.methodElement, 'Bound closure1.');
+ }
+ if (cls.methodElement.isInstanceMember) {
+ compiler.internalError(cls.methodElement, 'Bound closure2.');
+ }
+ }
+
+ // We fill the lists depending on the selector. For example,
+ // take method foo:
+ // foo(a, b, {c, d});
+ //
+ // We may have multiple ways of calling foo:
+ // (1) foo(1, 2);
+ // (2) foo(1, 2, c: 3);
+ // (3) foo(1, 2, d: 4);
+ // (4) foo(1, 2, c: 3, d: 4);
+ // (5) foo(1, 2, d: 4, c: 3);
+ //
+ // What we generate at the call sites are:
+ // (1) foo$2(1, 2);
+ // (2) foo$3$c(1, 2, 3);
+ // (3) foo$3$d(1, 2, 4);
+ // (4) foo$4$c$d(1, 2, 3, 4);
+ // (5) foo$4$c$d(1, 2, 3, 4);
+ //
+ // The stubs we generate are (expressed in Dart):
+ // (1) foo$2(a, b) => foo$4$c$d(a, b, null, null)
+ // (2) foo$3$c(a, b, c) => foo$4$c$d(a, b, c, null);
+ // (3) foo$3$d(a, b, d) => foo$4$c$d(a, b, null, d);
+ // (4) No stub generated, call is direct.
+ // (5) No stub generated, call is direct.
+ //
+ // We need to pay attention if this stub is for a function that has been
+ // invoked from a subclass. Then we cannot just redirect, since that
+ // would invoke the methods of the subclass. We have to compile to:
+ // (1) foo$2(a, b) => MyClass.foo$4$c$d.call(this, a, b, null, null)
+ // (2) foo$3$c(a, b, c) => MyClass.foo$4$c$d(this, a, b, c, null);
+ // (3) foo$3$d(a, b, d) => MyClass.foo$4$c$d(this, a, b, null, d);
+
+ Set<Selector> selectors = member.isInstanceMember
+ ? compiler.codegenWorld.invokedNames[member.name]
+ : null; // No stubs needed for static methods.
+
+ /// Returns all closure call selectors renamed to match this member.
+ Set<Selector> callSelectorsAsNamed() {
+ if (!canTearOff) return null;
+ Set<Selector> callSelectors = compiler.codegenWorld.invokedNames[
+ namer.closureInvocationSelectorName];
+ if (callSelectors == null) return null;
+ return callSelectors.map((Selector callSelector) {
+ return new Selector.call(
+ member.name, member.library,
+ callSelector.argumentCount, callSelector.namedArguments);
+ }).toSet();
+ }
+ if (selectors == null) {
+ selectors = callSelectorsAsNamed();
+ if (selectors == null) return generatedStubs;
+ } else {
+ Set<Selector> callSelectors = callSelectorsAsNamed();
+ if (callSelectors != null) {
+ selectors = selectors.union(callSelectors);
+ }
+ }
+ Set<Selector> untypedSelectors = new Set<Selector>();
+ if (selectors != null) {
+ for (Selector selector in selectors) {
+ if (!selector.appliesUnnamed(member, compiler.world)) continue;
+ if (untypedSelectors.add(selector.asUntyped)) {
+ jsAst.Expression stub = generateParameterStub(member, selector);
+ if (stub != null) {
+ generatedStubs[selector] = stub;
+ }
+ }
+ }
+ }
+ if (canTearOff) {
+ selectors = compiler.codegenWorld.invokedNames[
+ namer.closureInvocationSelectorName];
+ if (selectors != null) {
+ for (Selector selector in selectors) {
+ selector = new Selector.call(
+ member.name, member.library,
+ selector.argumentCount, selector.namedArguments);
+ if (!selector.appliesUnnamed(member, compiler.world)) continue;
+ if (untypedSelectors.add(selector)) {
+ jsAst.Expression stub = generateParameterStub(member, selector);
+ if (stub != null) {
+ generatedStubs[selector] = stub;
+ }
+ }
+ }
+ }
+ }
+ return generatedStubs;
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698