Version 1.3.0-dev.7.9
svn merge -c 34598 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
git-svn-id: http://dart.googlecode.com/svn/trunk@34646 260f80e4-7a28-3924-810f-c04153c831b5
diff --git a/sdk/lib/_internal/compiler/implementation/js_emitter/reflection_data_parser.dart b/sdk/lib/_internal/compiler/implementation/js_emitter/reflection_data_parser.dart
index 8605883..1e7210f 100644
--- a/sdk/lib/_internal/compiler/implementation/js_emitter/reflection_data_parser.dart
+++ b/sdk/lib/_internal/compiler/implementation/js_emitter/reflection_data_parser.dart
@@ -105,6 +105,7 @@
if (getterStubName) {
f = tearOff(funcs, array, isStatic, name, isIntercepted);
+ f.getterStub = true;
'''
/* Used to create an isolate using spawnFunction.*/
'''
@@ -114,28 +115,29 @@
if (getterStubName) functions.push(getterStubName);
f.\$stubName = getterStubName;
f.\$callName = null;
+ if (isIntercepted) init.interceptedNames[getterStubName] = true;
}
if (isReflectable) {
for (var i = 0; i < funcs.length; i++) {
funcs[i].$reflectableField = 1;
funcs[i].$reflectionInfoField = array;
}
- }
- if (isReflectable) {
+ var mangledNames = isStatic ? init.mangledGlobalNames : init.mangledNames;
var unmangledName = ${readString("array", "unmangledNameIndex")};
- var reflectionName =''' // Break long line.
- ''' unmangledName + ":" + requiredParameterCount +''' // Break long line.
+'''
+ // The function is either a getter, a setter, or a method.
+ // If it is a method, it might also have a tear-off closure.
+ // The unmangledName is the same as the getter-name.
+'''
+ var reflectionName = unmangledName;
+ if (getterStubName) mangledNames[getterStubName] = reflectionName;
+ if (isSetter) {
+ reflectionName += "=";
+ } else if (!isGetter) {
+ reflectionName += ":" + requiredParameterCount +''' // Break long line.
''' ":" + optionalParameterCount;
- if (isGetter) {
- reflectionName = unmangledName;
- } else if (isSetter) {
- reflectionName = unmangledName + "=";
}
- if (isStatic) {
- init.mangledGlobalNames[name] = reflectionName;
- } else {
- init.mangledNames[name] = reflectionName;
- }
+ mangledNames[name] = reflectionName;
funcs[0].$reflectionNameField = reflectionName;
funcs[0].$metadataIndexField = unmangledNameIndex + 1;
if (optionalParameterCount) descriptor[unmangledName + "*"] = funcs[0];
@@ -200,6 +202,7 @@
if (!init.statics) init.statics = map();
if (!init.typeInformation) init.typeInformation = map();
if (!init.globalFunctions) init.globalFunctions = map();
+ if (!init.interceptedNames) init.interceptedNames = map();
var libraries = init.libraries;
var mangledNames = init.mangledNames;
var mangledGlobalNames = init.mangledGlobalNames;
diff --git a/sdk/lib/_internal/lib/js_helper.dart b/sdk/lib/_internal/lib/js_helper.dart
index ed7f3c9..1ee856e 100644
--- a/sdk/lib/_internal/lib/js_helper.dart
+++ b/sdk/lib/_internal/lib/js_helper.dart
@@ -182,8 +182,14 @@
// critical, we might want to dynamically change [interceptedNames]
// to be a JavaScript object with intercepted names as property
// instead of a JavaScript array.
+ // TODO(floitsch): we already add stubs (tear-off getters) as properties
+ // in init.interceptedNames.
+ // Finish the transition and always use the object as hashtable.
bool isIntercepted =
- JS('int', '#.indexOf(#)', interceptedNames, name) != -1;
+ JS("bool",
+ 'Object.prototype.hasOwnProperty.call(init.interceptedNames, #) ||'
+ '#.indexOf(#) !== -1',
+ name, interceptedNames, name);
if (isIntercepted) {
receiver = interceptor;
if (JS('bool', '# === #', object, interceptor)) {
@@ -210,7 +216,12 @@
isCatchAll = true;
}
if (JS('bool', 'typeof # == "function"', method)) {
- if (!hasReflectableProperty(method)) {
+ // TODO(floitsch): bound or tear-off closure does not guarantee that the
+ // function is reflectable.
+ bool isReflectable = hasReflectableProperty(method) ||
+ object is BoundClosure ||
+ object is TearOffClosure;
+ if (!isReflectable) {
throwInvalidReflectionError(_symbol_dev.Symbol.getName(memberName));
}
if (isCatchAll) {
@@ -1970,6 +1981,7 @@
isIntercepted = true;
}
trampoline = forwardCallTo(receiver, function, isIntercepted);
+ JS('', '#.\$reflectionInfo = #', trampoline, reflectionInfo);
} else {
JS('', '#.\$name = #', prototype, propertyName);
}
@@ -2007,7 +2019,7 @@
}
}
- JS('', '#["call*"] = #', prototype, function);
+ JS('', '#["call*"] = #', prototype, trampoline);
return constructor;
}
diff --git a/sdk/lib/_internal/lib/js_mirrors.dart b/sdk/lib/_internal/lib/js_mirrors.dart
index 6876993..f42ea42 100644
--- a/sdk/lib/_internal/lib/js_mirrors.dart
+++ b/sdk/lib/_internal/lib/js_mirrors.dart
@@ -386,10 +386,13 @@
String name = _functions[i];
var jsFunction = JS('', '#[#]', _globalObject, name);
String unmangledName = mangledGlobalNames[name];
- if (unmangledName == null) {
+ if (unmangledName == null ||
+ JS('bool', "!!#['getterStub']", jsFunction)) {
// If there is no unmangledName, [jsFunction] is either a synthetic
// implementation detail, or something that is excluded
// by @MirrorsUsed.
+ // If it has a getterStub property it is a synthetic stub.
+ // TODO(floitsch): Remove the getterStub hack.
continue;
}
bool isConstructor = unmangledName.startsWith('new ');
diff --git a/tests/compiler/dart2js_extra/dart2js_extra.status b/tests/compiler/dart2js_extra/dart2js_extra.status
index ae5c042..409e87d 100644
--- a/tests/compiler/dart2js_extra/dart2js_extra.status
+++ b/tests/compiler/dart2js_extra/dart2js_extra.status
@@ -10,6 +10,7 @@
no_such_method_test: Fail # Wrong Invocation.memberName.
deferred/deferred_constant_test: Fail # http://dartbug.com/11138
constant_javascript_semantics4_test: Fail, OK
+mirrors_used_closure_test: Fail # Issue 17939
[ $compiler == dart2js && $runtime == jsshell ]
mirror_printer_test: Pass, Slow # Issue 16473
diff --git a/tests/compiler/dart2js_extra/mirrors_used_closure_test.dart b/tests/compiler/dart2js_extra/mirrors_used_closure_test.dart
new file mode 100644
index 0000000..28c19a8
--- /dev/null
+++ b/tests/compiler/dart2js_extra/mirrors_used_closure_test.dart
@@ -0,0 +1,32 @@
+// 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.
+
+library MirrorsTest;
+
+import 'package:expect/expect.dart';
+
+@MirrorsUsed(targets: const ['A.foo', 'B.bar'])
+import 'dart:mirrors';
+
+class A {
+ foo() => 42;
+ bar() => 499;
+}
+
+class B {
+ bar() => 33;
+}
+
+// Uses DateTime.now to make it impossible to predict.
+// Uses recursive call to make it harder to inline.
+confuse(x) {
+ if (new DateTime.now().millisecondsSinceEpoch == 42) return confuse(x + 1);
+ return x;
+}
+
+main() {
+ var f = [new A(), new B()][confuse(0)].bar;
+ Expect.throws(() => reflect(f).invoke(#call, [], {}),
+ (e) => e is UnsupportedError);
+}
diff --git a/tests/lib/lib.status b/tests/lib/lib.status
index 4d091a2..f55445e 100644
--- a/tests/lib/lib.status
+++ b/tests/lib/lib.status
@@ -36,7 +36,6 @@
mirrors/constructor_kinds_test: RuntimeError # Issue 13799
mirrors/constructor_private_name_test: CompileTimeError # Issue 13597
mirrors/delegate_call_through_getter_test: RuntimeError # Issue 15138
-mirrors/delegate_function_invocation_test: RuntimeError # Issue 6490
mirrors/equality_test/02: RuntimeError # Issue 12785
mirrors/fake_function_with_call_test: RuntimeError # Issue 11612
mirrors/fake_function_without_call_test: RuntimeError # Issue 11612
@@ -64,7 +63,7 @@
mirrors/invoke_call_through_getter_test: RuntimeError # Issue 15138
mirrors/invoke_call_through_implicit_getter_previously_accessed_test: RuntimeError # Issue 15138
mirrors/invoke_call_through_implicit_getter_test: RuntimeError # Issue 15138
-mirrors/invoke_closurization_test: RuntimeError # Issue 13002
+mirrors/invoke_closurization_test/static: RuntimeError # Issue 13002. When updating this status, please remove the "///" lines in the test.
mirrors/invoke_named_test/none: RuntimeError # Issue 12863
mirrors/invoke_private_test: CompileTimeError # Issue 12164
mirrors/invoke_private_wrong_library_test: CompileTimeError # Issue 12164
diff --git a/tests/lib/mirrors/invoke_closurization2_test.dart b/tests/lib/mirrors/invoke_closurization2_test.dart
new file mode 100644
index 0000000..b768fba
--- /dev/null
+++ b/tests/lib/mirrors/invoke_closurization2_test.dart
@@ -0,0 +1,130 @@
+// 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.
+
+library test.invoke_closurization_test;
+
+import 'dart:mirrors';
+
+import 'package:expect/expect.dart';
+
+class A {
+ foo() => "foo";
+ bar([x]) => "bar-$x";
+ gee({named}) => "gee-$named";
+
+ // Methods that must be intercepted.
+
+ // Tear-offs we will also get without mirrors.
+ codeUnitAt(x) => "codeUnitAt-$x";
+ toUpperCase() => "toUpperCase";
+ // indexOf takes an optional argument in String.
+ indexOf(x) => "indexOf-$x";
+ // lastIndexOf matches signature from String.
+ lastIndexOf(x, [y]) => "lastIndexOf-$x,$y";
+ // splitMapJoin matches signature from String.
+ splitMapJoin(x, {onMatch, onNonMatch}) =>
+ "splitMapJoin-$x,$onMatch,$onNonMatch";
+ // Same name as intercepted, but with named argument.
+ trim({named}) => "trim-$named";
+
+ // Tear-offs we will not call directly.
+ endsWith(x) => "endsWith-$x";
+ toLowerCase() => "toLowerCase";
+ // matchAsPrefix matches signature from String.
+ matchAsPrefix(x, [y = 0]) => "matchAsPrefix-$x,$y";
+ // Matches signature from List
+ toList({growable: true}) => "toList-$growable";
+ // Same name as intercepted, but with named argument.
+ toSet({named}) => "toSet-$named";
+}
+
+// The recursive call makes inlining difficult.
+// The use of DateTime.now makes the result unpredictable.
+confuse(x) {
+ if (new DateTime.now().millisecondsSinceEpoch == 42) {
+ return confuse(new DateTime.now().millisecondsSinceEpoch);
+ }
+ return x;
+}
+
+main() {
+ var list = ["foo", new List(), new A()];
+
+ getAMirror() => reflect(list[confuse(2)]);
+
+ // Tear-off without mirrors.
+ var f = confuse(getAMirror().reflectee.codeUnitAt);
+ Expect.equals("codeUnitAt-42", f(42));
+ f = confuse(getAMirror().reflectee.toUpperCase);
+ Expect.equals("toUpperCase", f());
+ f = confuse(getAMirror().reflectee.indexOf);
+ Expect.equals("indexOf-499", f(499));
+ f = confuse(getAMirror().reflectee.lastIndexOf);
+ Expect.equals("lastIndexOf-FOO,BAR", f("FOO", "BAR"));
+ f = confuse(getAMirror().reflectee.splitMapJoin);
+ Expect.equals("splitMapJoin-1,2,3", f(1, onMatch: 2, onNonMatch: 3));
+ f = confuse(getAMirror().reflectee.trim);
+ Expect.equals("trim-true", f(named: true));
+
+ // Now the same thing through mirrors.
+ f = getAMirror().getField(#codeUnitAt).reflectee;
+ Expect.equals("codeUnitAt-42", f(42));
+ f = getAMirror().getField(#toUpperCase).reflectee;
+ Expect.equals("toUpperCase", f());
+ f = getAMirror().getField(#indexOf).reflectee;
+ Expect.equals("indexOf-499", f(499));
+ f = getAMirror().getField(#lastIndexOf).reflectee;
+ Expect.equals("lastIndexOf-FOO,BAR", f("FOO", "BAR"));
+ f = getAMirror().getField(#splitMapJoin).reflectee;
+ Expect.equals("splitMapJoin-1,2,3", f(1, onMatch: 2, onNonMatch: 3));
+ f = getAMirror().getField(#trim).reflectee;
+ Expect.equals("trim-true", f(named: true));
+
+ // Now the same thing through mirrors and mirror-invocation.
+ f = getAMirror().getField(#codeUnitAt);
+ Expect.equals("codeUnitAt-42", f.invoke(#call, [42], {}).reflectee);
+ f = getAMirror().getField(#toUpperCase);
+ Expect.equals("toUpperCase", f.invoke(#call, [], {}).reflectee);
+ f = getAMirror().getField(#indexOf);
+ Expect.equals("indexOf-499", f.invoke(#call, [499], {}).reflectee);
+ f = getAMirror().getField(#lastIndexOf);
+ Expect.equals("lastIndexOf-FOO,BAR",
+ f.invoke(#call, ["FOO", "BAR"]).reflectee);
+ f = getAMirror().getField(#splitMapJoin);
+ Expect.equals("splitMapJoin-1,2,3",
+ f.invoke(#call, [1], {#onMatch: 2, #onNonMatch: 3}).reflectee);
+ f = getAMirror().getField(#trim);
+ Expect.equals("trim-true", f.invoke(#call, [], {#named: true}).reflectee);
+
+ // Tear-offs only through mirrors. (No direct selector in the code).
+ // --------
+
+ f = getAMirror().getField(#endsWith).reflectee;
+ Expect.equals("endsWith-42", f(42));
+ f = getAMirror().getField(#toLowerCase).reflectee;
+ Expect.equals("toLowerCase", f());
+ f = getAMirror().getField(#indexOf).reflectee;
+ Expect.equals("indexOf-499", f(499));
+ f = getAMirror().getField(#matchAsPrefix).reflectee;
+ Expect.equals("matchAsPrefix-FOO,BAR", f("FOO", "BAR"));
+ f = getAMirror().getField(#toList).reflectee;
+ Expect.equals("toList-1", f(growable: 1));
+ f = getAMirror().getField(#toSet).reflectee;
+ Expect.equals("toSet-true", f(named: true));
+
+ f = getAMirror().getField(#endsWith);
+ Expect.equals("endsWith-42", f.invoke(#call, [42], {}).reflectee);
+ f = getAMirror().getField(#toLowerCase);
+ Expect.equals("toLowerCase", f.invoke(#call, [], {}).reflectee);
+ f = getAMirror().getField(#indexOf);
+ Expect.equals("indexOf-499", f.invoke(#call, [499], {}).reflectee);
+ f = getAMirror().getField(#matchAsPrefix);
+ Expect.equals("matchAsPrefix-FOO,BAR",
+ f.invoke(#call, ["FOO", "BAR"]).reflectee);
+ f = getAMirror().getField(#toList);
+ Expect.equals("toList-1",
+ f.invoke(#call, [], {#growable: 1}).reflectee);
+ f = getAMirror().getField(#toSet);
+ Expect.equals("toSet-true", f.invoke(#call, [], {#named: true}).reflectee);
+}
diff --git a/tests/lib/mirrors/invoke_closurization_test.dart b/tests/lib/mirrors/invoke_closurization_test.dart
index 1039161..0b25ff0 100644
--- a/tests/lib/mirrors/invoke_closurization_test.dart
+++ b/tests/lib/mirrors/invoke_closurization_test.dart
@@ -7,7 +7,6 @@
import 'dart:mirrors';
import 'package:expect/expect.dart';
-import 'invoke_test.dart';
class C {
instanceMethod(x, y, z) => '$x+$y+$z';
@@ -25,14 +24,14 @@
Expect.equals("A+B+C", result.reflectee('A', 'B', 'C'));
ClassMirror cm = reflectClass(C);
- result = cm.getField(#staticFunction);
- Expect.isTrue(result.reflectee is Function, "Should be closure");
- Expect.equals("A-B-C", result.reflectee('A', 'B', 'C'));
+ result = cm.getField(#staticFunction); /// static: ok
+ Expect.isTrue(result.reflectee is Function, "Should be closure"); /// static: continued
+ Expect.equals("A-B-C", result.reflectee('A', 'B', 'C')); /// static: continued
LibraryMirror lm = cm.owner;
- result = lm.getField(#libraryFunction);
- Expect.isTrue(result.reflectee is Function, "Should be closure");
- Expect.equals("A:B:C", result.reflectee('A', 'B', 'C'));
+ result = lm.getField(#libraryFunction); /// static: continued
+ Expect.isTrue(result.reflectee is Function, "Should be closure"); /// static: continued
+ Expect.equals("A:B:C", result.reflectee('A', 'B', 'C')); /// static: continued
}
main() {
diff --git a/tools/VERSION b/tools/VERSION
index 78d81a4..22844ac 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -28,4 +28,4 @@
MINOR 3
PATCH 0
PRERELEASE 7
-PRERELEASE_PATCH 8
+PRERELEASE_PATCH 9