Add a new interceptor class JsArray, and support intercepting some list methods with the new interceptor scheme.
Review URL: https://codereview.chromium.org//11275316
git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@15009 260f80e4-7a28-3924-810f-c04153c831b5
diff --git a/sdk/lib/_internal/compiler/implementation/compile_time_constants.dart b/sdk/lib/_internal/compiler/implementation/compile_time_constants.dart
index 3505466..1f16080 100644
--- a/sdk/lib/_internal/compiler/implementation/compile_time_constants.dart
+++ b/sdk/lib/_internal/compiler/implementation/compile_time_constants.dart
@@ -38,6 +38,8 @@
String get name => 'ConstantHandler';
void registerCompileTimeConstant(Constant constant) {
+ compiler.enqueuer.codegen.registerInstantiatedClass(
+ constant.computeType(compiler).element);
compiledConstants.add(constant);
}
diff --git a/sdk/lib/_internal/compiler/implementation/enqueue.dart b/sdk/lib/_internal/compiler/implementation/enqueue.dart
index 2c51c32..8ed7bdb 100644
--- a/sdk/lib/_internal/compiler/implementation/enqueue.dart
+++ b/sdk/lib/_internal/compiler/implementation/enqueue.dart
@@ -138,12 +138,6 @@
}
void registerInstantiatedClass(ClassElement cls) {
- if (cls.isInterface()) {
- compiler.internalErrorOnElement(
- // Use the current element, as this is where cls is referenced from.
- compiler.currentElement,
- 'Expected a class, but $cls is an interface.');
- }
universe.instantiatedClasses.add(cls);
onRegisterInstantiatedClass(cls);
compiler.backend.registerInstantiatedClass(cls, this);
@@ -239,9 +233,7 @@
if (seenClasses.contains(cls)) continue;
seenClasses.add(cls);
cls.ensureResolved(compiler);
- if (!cls.isInterface()) {
- cls.implementation.forEachMember(processInstantiatedClassMember);
- }
+ cls.implementation.forEachMember(processInstantiatedClassMember);
if (isResolutionQueue) {
compiler.resolver.checkMembers(cls);
}
diff --git a/sdk/lib/_internal/compiler/implementation/js_backend/backend.dart b/sdk/lib/_internal/compiler/implementation/js_backend/backend.dart
index 74c7d41..5cc96d6 100644
--- a/sdk/lib/_internal/compiler/implementation/js_backend/backend.dart
+++ b/sdk/lib/_internal/compiler/implementation/js_backend/backend.dart
@@ -647,6 +647,7 @@
CodeEmitterTask emitter;
ClassElement jsStringClass;
+ ClassElement jsArrayClass;
ClassElement objectInterceptorClass;
Element getInterceptorMethod;
@@ -716,7 +717,7 @@
bool isInterceptorClass(Element element) {
if (element == null) return false;
- return element == jsStringClass;
+ return element == jsStringClass || element == jsArrayClass;
}
void addInterceptedSelector(Selector selector) {
@@ -749,6 +750,13 @@
initializeInterceptorElements();
}
result = jsStringClass;
+ } else if (cls == compiler.listClass) {
+ if (jsArrayClass == null) {
+ jsArrayClass =
+ compiler.findInterceptor(const SourceString('JSArray'));
+ initializeInterceptorElements();
+ }
+ result = jsArrayClass;
}
if (result == null) return;
diff --git a/sdk/lib/_internal/compiler/implementation/lib/interceptors.dart b/sdk/lib/_internal/compiler/implementation/lib/interceptors.dart
index 1f00319..ca685dc 100644
--- a/sdk/lib/_internal/compiler/implementation/lib/interceptors.dart
+++ b/sdk/lib/_internal/compiler/implementation/lib/interceptors.dart
@@ -6,6 +6,7 @@
import 'dart:collection';
+part 'js_array.dart';
part 'js_string.dart';
/**
@@ -23,47 +24,10 @@
*/
getInterceptor(object) {
if (object is String) return const JSString();
+ if (isJsArray(object)) return const JSArray();
return const ObjectInterceptor();
}
-add$1(var receiver, var value) {
- if (isJsArray(receiver)) {
- checkGrowable(receiver, 'add');
- JS('void', r'#.push(#)', receiver, value);
- return;
- }
- return UNINTERCEPTED(receiver.add(value));
-}
-
-removeAt$1(var receiver, int index) {
- if (isJsArray(receiver)) {
- if (index is !int) throw new ArgumentError(index);
- if (index < 0 || index >= receiver.length) {
- throw new RangeError.value(index);
- }
- checkGrowable(receiver, 'removeAt');
- return JS('var', r'#.splice(#, 1)[0]', receiver, index);
- }
- return UNINTERCEPTED(receiver.removeAt(index));
-}
-
-removeLast(var receiver) {
- if (isJsArray(receiver)) {
- checkGrowable(receiver, 'removeLast');
- if (receiver.length == 0) throw new RangeError.value(-1);
- return JS('var', r'#.pop()', receiver);
- }
- return UNINTERCEPTED(receiver.removeLast());
-}
-
-filter(var receiver, var predicate) {
- if (!isJsArray(receiver)) {
- return UNINTERCEPTED(receiver.filter(predicate));
- } else {
- return Collections.filter(receiver, [], predicate);
- }
-}
-
get$length(var receiver) {
if (receiver is String || isJsArray(receiver)) {
return JS('num', r'#.length', receiver); // TODO(sra): Use 'int'?
@@ -103,13 +67,6 @@
return JS('String', r'String(#)', value);
}
-iterator(receiver) {
- if (isJsArray(receiver)) {
- return new ListIterator(receiver);
- }
- return UNINTERCEPTED(receiver.iterator());
-}
-
get$isEmpty(receiver) {
if (receiver is String || isJsArray(receiver)) {
return JS('bool', r'#.length === 0', receiver);
@@ -149,70 +106,11 @@
}
}
-addAll(receiver, collection) {
- if (!isJsArray(receiver)) return UNINTERCEPTED(receiver.addAll(collection));
-
- // TODO(ahe): Use for-in when it is implemented correctly.
- var iterator = collection.iterator();
- while (iterator.hasNext) {
- receiver.add(iterator.next());
+iterator(receiver) {
+ if (isJsArray(receiver)) {
+ return new ListIterator(receiver);
}
-}
-
-addLast(receiver, value) {
- if (!isJsArray(receiver)) return UNINTERCEPTED(receiver.addLast(value));
-
- checkGrowable(receiver, 'addLast');
- JS('void', r'#.push(#)', receiver, value);
-}
-
-clear(receiver) {
- if (!isJsArray(receiver)) return UNINTERCEPTED(receiver.clear());
- receiver.length = 0;
-}
-
-forEach(receiver, f) {
- if (!isJsArray(receiver)) {
- return UNINTERCEPTED(receiver.forEach(f));
- } else {
- return Collections.forEach(receiver, f);
- }
-}
-
-map(receiver, f) {
- if (!isJsArray(receiver)) {
- return UNINTERCEPTED(receiver.map(f));
- } else {
- return Collections.map(receiver, [], f);
- }
-}
-
-reduce(receiver, initialValue, f) {
- if (!isJsArray(receiver)) {
- return UNINTERCEPTED(receiver.reduce(initialValue, f));
- } else {
- return Collections.reduce(receiver, initialValue, f);
- }
-}
-
-getRange(receiver, start, length) {
- if (!isJsArray(receiver)) {
- return UNINTERCEPTED(receiver.getRange(start, length));
- }
- if (0 == length) return [];
- checkNull(start); // TODO(ahe): This is not specified but co19 tests it.
- checkNull(length); // TODO(ahe): This is not specified but co19 tests it.
- if (start is !int) throw new ArgumentError(start);
- if (length is !int) throw new ArgumentError(length);
- if (length < 0) throw new ArgumentError(length);
- if (start < 0) throw new RangeError.value(start);
- var end = start + length;
- if (end > receiver.length) {
- throw new RangeError.value(length);
- }
- if (length < 0) throw new ArgumentError(length);
- // TODO(sra): We need a type that is exactly the JavaScript Array type.
- return JS('=List', r'#.slice(#, #)', receiver, start, end);
+ return UNINTERCEPTED(receiver.iterator());
}
indexOf$1(receiver, element) {
diff --git a/sdk/lib/_internal/compiler/implementation/lib/js_array.dart b/sdk/lib/_internal/compiler/implementation/lib/js_array.dart
new file mode 100644
index 0000000..9f5db9b
--- /dev/null
+++ b/sdk/lib/_internal/compiler/implementation/lib/js_array.dart
@@ -0,0 +1,83 @@
+// Copyright (c) 2012, 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 _interceptors;
+
+/**
+ * The interceptor class for [List]. The compiler recognizes this
+ * class as an interceptor, and changes references to [:this:] to
+ * actually use the receiver of the method, which is generated as an extra
+ * argument added to each member.
+ */
+class JSArray<E> implements List<E> {
+ const JSArray();
+
+ void add(E value) {
+ checkGrowable(this, 'add');
+ JS('void', r'#.push(#)', this, value);
+ }
+
+ E removeAt(int index) {
+ if (index is !int) throw new ArgumentError(index);
+ if (index < 0 || index >= length) {
+ throw new RangeError.value(index);
+ }
+ checkGrowable(this, 'removeAt');
+ return JS('var', r'#.splice(#, 1)[0]', this, index);
+ }
+
+ E removeLast() {
+ checkGrowable(this, 'removeLast');
+ if (length == 0) throw new RangeError.value(-1);
+ return JS('var', r'#.pop()', this);
+ }
+
+ List<E> filter(bool f(E element)) {
+ return Collections.filter(this, <E>[], f);
+ }
+
+ void addAll(Collection<E> collection) {
+ for (Element e in collection) {
+ this.add(e);
+ }
+ }
+
+ void addLast(E value) {
+ checkGrowable(this, 'addLast');
+ JS('void', r'#.push(#)', this, value);
+ }
+
+ void clear() {
+ length = 0;
+ }
+
+ void forEach(void f(E element)) {
+ return Collections.forEach(this, f);
+ }
+
+ Collection map(f(E element)) {
+ return Collections.map(this, [], f);
+ }
+
+ reduce(initialValue, combine(previousValue, E element)) {
+ return Collections.reduce(this, initialValue, combine);
+ }
+
+ List<E> getRange(int start, int length) {
+ // TODO(ngeoffray): Parameterize the return value.
+ if (0 == length) return [];
+ checkNull(start); // TODO(ahe): This is not specified but co19 tests it.
+ checkNull(length); // TODO(ahe): This is not specified but co19 tests it.
+ if (start is !int) throw new ArgumentError(start);
+ if (length is !int) throw new ArgumentError(length);
+ if (length < 0) throw new ArgumentError(length);
+ if (start < 0) throw new RangeError.value(start);
+ int end = start + length;
+ if (end > this.length) {
+ throw new RangeError.value(length);
+ }
+ if (length < 0) throw new ArgumentError(length);
+ return JS('List', r'#.slice(#, #)', this, start, end);
+ }
+}
diff --git a/sdk/lib/_internal/compiler/implementation/resolution/members.dart b/sdk/lib/_internal/compiler/implementation/resolution/members.dart
index 8dc19bf..d2c8c11 100644
--- a/sdk/lib/_internal/compiler/implementation/resolution/members.dart
+++ b/sdk/lib/_internal/compiler/implementation/resolution/members.dart
@@ -2098,6 +2098,7 @@
}
visitLiteralList(LiteralList node) {
+ world.registerInstantiatedClass(compiler.listClass);
NodeList arguments = node.typeArguments;
if (arguments != null) {
Link<Node> nodes = arguments.nodes;
diff --git a/sdk/lib/_internal/compiler/implementation/ssa/builder.dart b/sdk/lib/_internal/compiler/implementation/ssa/builder.dart
index f47e5da..b6137f2 100644
--- a/sdk/lib/_internal/compiler/implementation/ssa/builder.dart
+++ b/sdk/lib/_internal/compiler/implementation/ssa/builder.dart
@@ -2291,6 +2291,17 @@
return result;
}
+ Element getInterceptor(Send send, Selector selector) {
+ if (!methodInterceptionEnabled) return null;
+ if (!backend.isInterceptorClass(currentElement.getEnclosingClass())
+ && send.receiver == null) {
+ // The call applies to [:this:] which can not be an interceptor
+ // object.
+ return null;
+ }
+ return interceptors.getStaticInterceptor(selector);
+ }
+
void generateInstanceGetterWithCompiledReceiver(Send send,
HInstruction receiver) {
assert(Elements.isInstanceSend(send, elements));
@@ -2302,10 +2313,8 @@
: elements.getSelector(send.selector);
assert(selector.isGetter());
SourceString getterName = selector.name;
- Element interceptor = null;
- if (methodInterceptionEnabled) {
- interceptor = interceptors.getStaticInterceptor(selector);
- }
+ Element interceptor = getInterceptor(send, selector);
+
bool hasGetter = compiler.world.hasAnyUserDefinedGetter(selector);
if (interceptor == backend.getInterceptorMethod && interceptor != null) {
// If we're using an interceptor class, emit a call to the
@@ -2386,10 +2395,7 @@
Selector selector = elements.getSelector(send);
assert(selector.isSetter());
SourceString setterName = selector.name;
- Element interceptor = null;
- if (methodInterceptionEnabled) {
- interceptor = interceptors.getStaticInterceptor(selector);
- }
+ Element interceptor = getInterceptor(send, selector);
bool hasSetter = compiler.world.hasAnyUserDefinedSetter(selector);
if (interceptor != null && interceptor == backend.getInterceptorMethod) {
compiler.internalError(
@@ -2659,10 +2665,7 @@
dartMethodName = node.selector.asIdentifier().source;
}
- Element interceptor = null;
- if (methodInterceptionEnabled) {
- interceptor = interceptors.getStaticInterceptor(selector);
- }
+ Element interceptor = getInterceptor(node, selector);
if (interceptor != null) {
if (interceptor == backend.getInterceptorMethod) {