blob: d7e1578c9962a0ecc46ba159681e45a0fd10f593 [file] [log] [blame]
// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
// 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 ClassStubGenerator {
final Namer namer;
final Compiler compiler;
final JavaScriptBackend backend;
ClassStubGenerator(this.compiler, this.namer, this.backend);
jsAst.Expression generateClassConstructor(ClassElement classElement,
Iterable<String> fields) {
// TODO(sra): Implement placeholders in VariableDeclaration position:
//
// String constructorName = namer.getNameOfClass(classElement);
// return js.statement('function #(#) { #; }',
// [ constructorName, fields,
// fields.map(
// (name) => js('this.# = #', [name, name]))]));
return js('function(#) { #; }',
[fields,
fields.map((name) => js('this.# = #', [name, name]))]);
}
jsAst.Expression generateGetter(Element member, String fieldName) {
ClassElement cls = member.enclosingClass;
String receiver = backend.isInterceptorClass(cls) ? 'receiver' : 'this';
List<String> args = backend.isInterceptedMethod(member) ? ['receiver'] : [];
return js('function(#) { return #.# }', [args, receiver, fieldName]);
}
jsAst.Expression generateSetter(Element member, String fieldName) {
ClassElement cls = member.enclosingClass;
String receiver = backend.isInterceptorClass(cls) ? 'receiver' : 'this';
List<String> args = backend.isInterceptedMethod(member) ? ['receiver'] : [];
// TODO(floitsch): remove 'return'?
return js('function(#, v) { return #.# = v; }',
[args, receiver, fieldName]);
}
/**
* Documentation wanted -- johnniwinther
*
* Invariant: [member] must be a declaration element.
*/
Map<String, jsAst.Expression> generateCallStubsForGetter(
Element member, Set<Selector> selectors) {
assert(invariant(member, member.isDeclaration));
// If the method is intercepted, the stub gets the
// receiver explicitely and we need to pass it to the getter call.
bool isInterceptedMethod = backend.isInterceptedMethod(member);
bool isInterceptorClass =
backend.isInterceptorClass(member.enclosingClass);
const String receiverArgumentName = r'$receiver';
jsAst.Expression buildGetter() {
jsAst.Expression receiver =
js(isInterceptorClass ? receiverArgumentName : 'this');
if (member.isGetter) {
String getterName = namer.getterName(member);
if (isInterceptedMethod) {
return js('this.#(#)', [getterName, receiver]);
}
return js('#.#()', [receiver, getterName]);
} else {
String fieldName = namer.instanceFieldPropertyName(member);
return js('#.#', [receiver, fieldName]);
}
}
Map<String, jsAst.Expression> generatedStubs = <String, jsAst.Expression>{};
// Two selectors may match but differ only in type. To avoid generating
// identical stubs for each we track untyped selectors which already have
// stubs.
Set<Selector> generatedSelectors = new Set<Selector>();
for (Selector selector in selectors) {
if (selector.applies(member, compiler.world)) {
selector = selector.asUntyped;
if (generatedSelectors.contains(selector)) continue;
generatedSelectors.add(selector);
String invocationName = namer.invocationName(selector);
Selector callSelector = new Selector.callClosureFrom(selector);
String closureCallName = namer.invocationName(callSelector);
List<jsAst.Parameter> parameters = <jsAst.Parameter>[];
List<jsAst.Expression> arguments = <jsAst.Expression>[];
if (isInterceptedMethod) {
parameters.add(new jsAst.Parameter(receiverArgumentName));
}
for (int i = 0; i < selector.argumentCount; i++) {
String name = 'arg$i';
parameters.add(new jsAst.Parameter(name));
arguments.add(js('#', name));
}
jsAst.Fun function = js(
'function(#) { return #.#(#); }',
[ parameters, buildGetter(), closureCallName, arguments]);
generatedStubs[invocationName] = function;
}
}
return generatedStubs;
}
}