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

Unified Diff: sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart

Issue 12330135: Make instance methods whose names collide with intercepted methods have the interceptor calling con… (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 7 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 side-by-side diff with in-line comments
Download patch
Index: sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart
===================================================================
--- sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart (revision 19306)
+++ sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart (working copy)
@@ -653,12 +653,11 @@
if (alreadyGenerated.contains(invocationName)) return;
alreadyGenerated.add(invocationName);
- bool isInterceptorClass =
- backend.isInterceptorClass(member.getEnclosingClass());
+ bool isInterceptedMethod = backend.isInterceptedMethod(member);
- // If the method is in an interceptor class, we need to also pass
+ // If the method is intercepted, we need to also pass
// the actual receiver.
- int extraArgumentCount = isInterceptorClass ? 1 : 0;
+ 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 '$'.
@@ -673,7 +672,7 @@
parameters.parameterCount + extraArgumentCount);
int count = 0;
- if (isInterceptorClass) {
+ if (isInterceptedMethod) {
count++;
parametersBuffer[0] = new jsAst.Parameter(receiverArgumentName);
argumentsBuffer[0] = js[receiverArgumentName];
@@ -913,11 +912,6 @@
void emitInstanceMembers(ClassElement classElement,
ClassBuilder builder) {
assert(invariant(classElement, classElement.isDeclaration));
- if (classElement == backend.objectInterceptorClass) {
- emitInterceptorMethods(builder);
- // The ObjectInterceptor does not have any instance methods.
- return;
- }
void visitMember(ClassElement enclosing, Element member) {
assert(invariant(classElement, member.isDeclaration));
@@ -995,13 +989,10 @@
}
}
- if (backend.isInterceptorClass(classElement)) {
- // The operator== method in [:Object:] does not take the same
- // number of arguments as an intercepted method, therefore we
- // explicitely add one to all interceptor classes. Note that we
- // would not have do do that if all intercepted methods had
- // a calling convention where the receiver is the first
- // parameter.
+ if (backend.isInterceptorClass(classElement)
+ && classElement != compiler.objectClass) {
+ // We optimize the operator== on interceptor classes to
+ // just do a JavaScript double or triple equals.
String name = backend.namer.publicInstanceMethodNameByArity(
const SourceString('=='), 1);
Function kind = (classElement == backend.jsNullClass)
@@ -1129,10 +1120,15 @@
? member.fixedBackendName()
: (isMixinNativeField ? member.name.slowToString() : accessorName);
bool needsCheckedSetter = false;
- if (needsSetter && compiler.enableTypeAssertions
- && canGenerateCheckedSetter(member)) {
- needsCheckedSetter = true;
- needsSetter = false;
+ if (needsSetter) {
+ if (compiler.enableTypeAssertions
+ && canGenerateCheckedSetter(member)) {
+ needsCheckedSetter = true;
+ needsSetter = false;
+ } else if (backend.isInterceptedMethod(member)) {
+ // The [addField] will take care of generating the setter.
+ needsSetter = false;
+ }
}
// Getters and setters with suffixes will be generated dynamically.
addField(member,
@@ -1170,6 +1166,7 @@
void generateGetter(Element member, String fieldName, String accessorName,
ClassBuilder builder) {
+ assert(!backend.isInterceptorClass(member));
String getterName = namer.getterNameFromAccessorName(accessorName);
builder.addProperty(getterName,
js.fun([], js.return_(js['this'][fieldName])));
@@ -1177,9 +1174,13 @@
void generateSetter(Element member, String fieldName, String accessorName,
ClassBuilder builder) {
+ assert(!backend.isInterceptorClass(member));
String setterName = namer.setterNameFromAccessorName(accessorName);
+ List<String> args = backend.isInterceptedMethod(member)
+ ? ['receiver', 'v']
+ : ['v'];
builder.addProperty(setterName,
- js.fun(['v'], js['this'][fieldName].assign('v')));
+ js.fun(args, js['this'][fieldName].assign('v')));
}
bool canGenerateCheckedSetter(Element member) {
@@ -1210,8 +1211,11 @@
}
String setterName = namer.setterNameFromAccessorName(accessorName);
+ List<String> args = backend.isInterceptedMethod(member)
+ ? ['receiver', 'v']
+ : ['v'];
builder.addProperty(setterName,
- js.fun(['v'], js['this'][fieldName].assign(js[helperName](arguments))));
+ js.fun(args, js['this'][fieldName].assign(js[helperName](arguments))));
}
void emitClassConstructor(ClassElement classElement, ClassBuilder builder) {
@@ -1303,6 +1307,9 @@
if (needsSetter) {
generateSetter(member, name, accessorName, builder);
}
+ } else if (backend.isInterceptedMethod(member)
+ && instanceFieldNeedsSetter(member)) {
+ generateSetter(member, name, accessorName, builder);
}
});
});
@@ -1362,38 +1369,6 @@
return _selectorRank(selector1) - _selectorRank(selector2);
}
- void emitInterceptorMethods(ClassBuilder builder) {
- // Emit forwarders for the ObjectInterceptor class. We need to
- // emit all possible sends on intercepted methods. Because of
- // typed selectors we have to avoid generating the same forwarder
- // multiple times.
- Set<String> alreadyGenerated = new Set<String>();
- for (Selector selector in
- backend.usedInterceptors.toList()..sort(_compareSelectorNames)) {
- String name = backend.namer.invocationName(selector);
- if (alreadyGenerated.contains(name)) continue;
- alreadyGenerated.add(name);
-
- List<jsAst.Parameter> parameters = <jsAst.Parameter>[];
- List<jsAst.Expression> arguments = <jsAst.Expression>[];
- parameters.add(new jsAst.Parameter('receiver'));
-
- if (selector.isSetter()) {
- parameters.add(new jsAst.Parameter('value'));
- arguments.add(js['value']);
- } else {
- for (int i = 0; i < selector.argumentCount; i++) {
- String argName = 'a$i';
- parameters.add(new jsAst.Parameter(argName));
- arguments.add(js[argName]);
- }
- }
- jsAst.Fun function =
- js.fun(parameters, js.return_(js['receiver'][name](arguments)));
- builder.addProperty(name, function);
- }
- }
-
Iterable<Element> getTypedefChecksOn(DartType type) {
bool isSubtype(TypedefElement typedef) {
FunctionType typedefType =
@@ -1560,8 +1535,9 @@
}
// Add unneeded interceptors to the [unneededClasses] set.
- for (ClassElement interceptor in backend.interceptedClasses.keys) {
- if (!needed.contains(interceptor)) {
+ for (ClassElement interceptor in backend.interceptedClasses) {
+ if (!needed.contains(interceptor)
+ && interceptor != compiler.objectClass) {
unneededClasses.add(interceptor);
}
}
@@ -1711,9 +1687,9 @@
Map<int, String> cache;
String extraArg = null;
- // Methods on interceptor classes take an extra parameter, which is the
- // actual receiver of the call.
- bool inInterceptor = backend.isInterceptorClass(member.getEnclosingClass());
+ // Intercepted methods take an extra parameter, which is the
+ // receiver of the call.
+ bool inInterceptor = backend.isInterceptedMethod(member);
if (inInterceptor) {
cache = interceptorClosureCache;
extraArg = 'receiver';
@@ -1834,10 +1810,9 @@
DefineStubFunction defineStub) {
assert(invariant(member, member.isDeclaration));
LibraryElement memberLibrary = member.getLibrary();
- // If the class is an interceptor class, the stub gets the
+ // If the method is intercepted, the stub gets the
// receiver explicitely and we need to pass it to the getter call.
- bool isInterceptorClass =
- backend.isInterceptorClass(member.getEnclosingClass());
+ bool isInterceptedMethod = backend.isInterceptedMethod(member);
const String receiverArgumentName = r'$receiver';
@@ -1845,7 +1820,7 @@
if (member.isGetter()) {
String getterName = namer.getterName(member);
return js['this'][getterName](
- isInterceptorClass
+ isInterceptedMethod
? <jsAst.Expression>[js[receiverArgumentName]]
: <jsAst.Expression>[]);
} else {
@@ -1873,7 +1848,7 @@
List<jsAst.Parameter> parameters = <jsAst.Parameter>[];
List<jsAst.Expression> arguments = <jsAst.Expression>[];
- if (isInterceptorClass) {
+ if (isInterceptedMethod) {
parameters.add(new jsAst.Parameter(receiverArgumentName));
}
@@ -1897,6 +1872,8 @@
Iterable<VariableElement> staticNonFinalFields =
handler.getStaticNonFinalFieldsForEmission();
for (Element element in Elements.sortedByPosition(staticNonFinalFields)) {
+ // [:interceptedNames:] is handled in [emitInterceptedNames].
+ if (element == backend.interceptedNames) continue;
compiler.withCurrentElement(element, () {
Constant initialValue = handler.getInitialValueFor(element);
jsAst.Expression init =
@@ -2035,14 +2012,19 @@
String createInvocationMirror = namer.getName(
compiler.createInvocationMirrorElement);
+ assert(backend.isInterceptedName(Compiler.NO_SUCH_METHOD));
jsAst.Expression expression = js['this.$noSuchMethodName'](
- js[namer.CURRENT_ISOLATE][createInvocationMirror]([
- js.string(methodName),
- js.string(internalName),
- type,
- new jsAst.ArrayInitializer.from(
- parameters.map((param) => js[param.name]).toList()),
- new jsAst.ArrayInitializer.from(argNames)]));
+ [js['this'],
+ js[namer.CURRENT_ISOLATE][createInvocationMirror]([
+ js.string(methodName),
+ js.string(internalName),
+ type,
+ new jsAst.ArrayInitializer.from(
+ parameters.map((param) => js[param.name]).toList()),
+ new jsAst.ArrayInitializer.from(argNames)])]);
+ parameters = backend.isInterceptedName(selector.name)
+ ? ([new jsAst.Parameter('\$receiver')]..addAll(parameters))
+ : parameters;
return js.fun(parameters, js.return_(expression));
}
@@ -2183,7 +2165,6 @@
}
void emitGetInterceptorMethod(CodeBuffer buffer,
- String objectName,
String key,
Collection<ClassElement> classes) {
jsAst.Statement buildReturnInterceptor(ClassElement cls) {
@@ -2235,10 +2216,11 @@
else if (cls == backend.jsNullClass) hasNull = true;
else if (cls == backend.jsNumberClass) hasNumber = true;
else if (cls == backend.jsStringClass) hasString = true;
- else throw 'Internal error: $cls';
+ else {
+ assert(cls == compiler.objectClass);
+ }
}
if (hasDouble) {
- assert(!hasNumber);
hasNumber = true;
}
if (hasInt) hasNumber = true;
@@ -2291,7 +2273,7 @@
if (hasArray) {
block.statements.add(buildInterceptorCheck(backend.jsArrayClass));
}
- block.statements.add(js.return_(js[objectName]['prototype']));
+ block.statements.add(js.return_(receiver));
buffer.add(jsAst.prettyPrint(
js[isolateProperties][key].assign(js.fun(['receiver'], block)),
@@ -2303,13 +2285,10 @@
* Emit all versions of the [:getInterceptor:] method.
*/
void emitGetInterceptorMethods(CodeBuffer buffer) {
- // If no class needs to be intercepted, just return.
- if (backend.objectInterceptorClass == null) return;
- String objectName = namer.isolateAccess(backend.objectInterceptorClass);
var specializedGetInterceptors = backend.specializedGetInterceptors;
for (String name in specializedGetInterceptors.keys.toList()..sort()) {
Collection<ClassElement> classes = specializedGetInterceptors[name];
- emitGetInterceptorMethod(buffer, objectName, name, classes);
+ emitGetInterceptorMethod(buffer, name, classes);
}
}
@@ -2535,7 +2514,7 @@
for (String name in names) {
Selector selector = backend.oneShotInterceptors[name];
Set<ClassElement> classes =
- backend.getInterceptedClassesOn(selector);
+ backend.getInterceptedClassesOn(selector.name);
String getInterceptorName =
namer.getInterceptorName(backend.getInterceptorMethod, classes);
@@ -2577,6 +2556,32 @@
}
}
+ /**
+ * If [:invokeOn:] has been compiled, emit all the possible selector names
+ * that are intercepted into the [:interceptedNames:] top-level
+ * variable. The implementation of [:invokeOn:] will use it to
+ * determine whether it should call the method with an extra
+ * parameter.
+ */
+ void emitInterceptedNames(CodeBuffer buffer) {
+ if (!compiler.enabledInvokeOn) return;
+ String name = backend.namer.getName(backend.interceptedNames);
+ jsAst.PropertyAccess property = js[isolateProperties][name];
+
+ int index = 0;
+ List<jsAst.ArrayElement> elements = backend.usedInterceptors.map(
+ (Selector selector) {
+ jsAst.Literal str = js.string(namer.invocationName(selector));
+ return new jsAst.ArrayElement(index++, str);
+ }).toList();
+ jsAst.ArrayInitializer array = new jsAst.ArrayInitializer(
+ backend.usedInterceptors.length,
+ elements);
+
+ buffer.add(jsAst.prettyPrint(property.assign(array), compiler));
+ buffer.add(N);
+ }
+
void emitInitFunction(CodeBuffer buffer) {
jsAst.Fun fun = js.fun([], [
js['$isolateProperties = {}'],
@@ -2678,6 +2683,7 @@
// constants to be set up.
emitStaticNonFinalFieldInitializations(mainBuffer);
emitOneShotInterceptors(mainBuffer);
+ emitInterceptedNames(mainBuffer);
emitGetInterceptorMethods(mainBuffer);
emitLazilyInitializedStaticFields(mainBuffer);

Powered by Google App Engine
This is Rietveld 408576698