Version 0.5.6.0
svn merge -r 22413:22491 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
git-svn-id: http://dart.googlecode.com/svn/trunk@22493 260f80e4-7a28-3924-810f-c04153c831b5
diff --git a/pkg/browser/lib/dart.js b/pkg/browser/lib/dart.js
index b507378..d610647 100644
--- a/pkg/browser/lib/dart.js
+++ b/pkg/browser/lib/dart.js
@@ -27,9 +27,9 @@
var script = document.createElement('script');
script.src = scripts[i].src.replace(/\.dart(?=\?|$)/, '.dart.js');
var parent = scripts[i].parentNode;
- // TODO(vsm): Find a solution for issue 8455 that works with more
- // than one script.
- document.currentScript = script;
+ // TODO(vsm): Find a solution for issue 8455 that works with more
+ // than one script.
+ document.currentScript = script;
parent.replaceChild(script, scripts[i]);
}
}
diff --git a/pkg/pkg.status b/pkg/pkg.status
index 53e34af..c1abb38 100644
--- a/pkg/pkg.status
+++ b/pkg/pkg.status
@@ -110,6 +110,9 @@
unittest/test/mock_regexp_negative_test: Fail
unittest/test/mock_stepwise_negative_test: Fail
+[ $compiler == dartanalyzer ]
+unittest/test/mock_regexp_negative_test: fail
+unittest/test/mock_stepwise_negative_test: fail
[ $compiler == dart2js ]
unittest/test/instance_test: Skip
@@ -140,3 +143,6 @@
# test on Dartium, which requires all tests to have a library.
[ $runtime == dartium || $runtime == drt ]
serialization/test/no_library_test: Skip # Expected Failure
+
+[ $compiler == none && ($runtime == dartium || $runtime == drt) ]
+unittest/test/unittest_test: Timeout # http://dartbug.com/10470
diff --git a/pkg/scheduled_test/test/substitute_future_test.dart b/pkg/scheduled_test/test/substitute_future_test.dart
index 7202c86..5cffb4b 100644
--- a/pkg/scheduled_test/test/substitute_future_test.dart
+++ b/pkg/scheduled_test/test/substitute_future_test.dart
@@ -148,7 +148,9 @@
var completer = new Completer();
var future = new SubstituteFuture(completer.future);
completer.complete('success');
- expect(() => future.substitute(new Future.value()),
- throwsStateError);
+ future.then(expectAsync1((_) {
+ expect(() => future.substitute(new Future.value()),
+ throwsStateError);
+ }));
});
}
diff --git a/pkg/scheduled_test/test/value_future_test.dart b/pkg/scheduled_test/test/value_future_test.dart
index 61ab436..3cf0fb9 100644
--- a/pkg/scheduled_test/test/value_future_test.dart
+++ b/pkg/scheduled_test/test/value_future_test.dart
@@ -98,30 +98,39 @@
});
test('.value is the result of the future', () {
- expect(future.value, equals(12));
+ future.then(expectAsync1((_) {
+ expect(future.value, equals(12));
+ }));
});
test('.hasValue is true', () {
- expect(future.hasValue, isTrue);
+ future.then(expectAsync1((_) {
+ expect(future.hasValue, isTrue);
+ }));
});
});
group('after an error completion', () {
var future;
+ var safeFuture;
setUp(() {
var completer = new Completer();
future = new ValueFuture(completer.future);
- future.catchError((e) {});
+ safeFuture = future.catchError((e) {});
completer.completeError('bad');
});
test('.value is null', () {
- expect(future.value, isNull);
+ safeFuture.then(expectAsync1((_) {
+ expect(future.value, isNull);
+ }));
});
test('.hasValue is false', () {
- expect(future.hasValue, isFalse);
+ safeFuture.then(expectAsync1((_) {
+ expect(future.hasValue, isFalse);
+ }));
});
});
}
diff --git a/pkg/unittest/lib/interactive_html_config.dart b/pkg/unittest/lib/interactive_html_config.dart
index 0f7ff03..1fcb07c 100644
--- a/pkg/unittest/lib/interactive_html_config.dart
+++ b/pkg/unittest/lib/interactive_html_config.dart
@@ -8,6 +8,9 @@
* IFrame, so the configuration consists of two parts - a 'parent'
* config that manages all the tests, and a 'child' config for the
* IFrame that runs the individual tests.
+ *
+ * Note: this unit test configuration will not work with the debugger (the tests
+ * are executed in a separate IFrame).
*/
library unittest_interactive_html_config;
@@ -462,8 +465,11 @@
}
/**
- * Allocate a Configuration. We allocate either a parent or
- * child, depending on whether the URL has a search part.
+ * Allocate a Configuration. We allocate either a parent or child, depending on
+ * whether the URL has a search part.
+ *
+ * Note: this unit test configuration will not work with the debugger (the tests
+ * are executed in a separate IFrame).
*/
void useInteractiveHtmlConfiguration() {
if (window.location.search == '') { // This is the parent.
diff --git a/pkg/unittest/lib/src/config.dart b/pkg/unittest/lib/src/config.dart
index 8b5a60c..490ef80 100644
--- a/pkg/unittest/lib/src/config.dart
+++ b/pkg/unittest/lib/src/config.dart
@@ -160,6 +160,28 @@
}
/**
+ * Format a test result.
+ */
+ String formatResult(TestCase testCase) {
+ var result = new StringBuffer();
+ result.write(testCase.result.toUpperCase());
+ result.write(": ");
+ result.write(testCase.description);
+ result.write("\n");
+
+ if (testCase.message != '') {
+ result.write(_indent(testCase.message));
+ result.write("\n");
+ }
+
+ if (testCase.stackTrace != null && testCase.stackTrace != '') {
+ result.write(_indent(testCase.stackTrace));
+ result.write("\n");
+ }
+ return result.toString();
+ }
+
+ /**
* Called with the result of all test cases. The default implementation prints
* the result summary using the built-in [print] command. Browser tests
* commonly override this to reformat the output.
@@ -171,16 +193,7 @@
String uncaughtError) {
// Print each test's result.
for (final t in results) {
- var resultString = "${t.result}".toUpperCase();
- print('$resultString: ${t.description}');
-
- if (t.message != '') {
- print(_indent(t.message));
- }
-
- if (t.stackTrace != null && t.stackTrace != '') {
- print(_indent(t.stackTrace));
- }
+ print(formatResult(t));
}
// Show the summary.
diff --git a/pkg/unittest/lib/vm_config.dart b/pkg/unittest/lib/vm_config.dart
index 9d2e6ea..d8b3f23 100644
--- a/pkg/unittest/lib/vm_config.dart
+++ b/pkg/unittest/lib/vm_config.dart
@@ -11,6 +11,32 @@
import 'unittest.dart';
class VMConfiguration extends Configuration {
+ // Color constants used for generating messages.
+ final String GREEN_COLOR = '\u001b[32m';
+ final String RED_COLOR = '\u001b[31m';
+ final String MAGENTA_COLOR = '\u001b[35m';
+ final String NO_COLOR = '\u001b[0m';
+
+ // We make this public so the user can turn it off if they want.
+ bool useColor;
+
+ VMConfiguration() :
+ super(), useColor = stdioType(stdout) == StdioType.TERMINAL;
+
+ String formatResult(TestCase testCase) {
+ String result = super.formatResult(testCase);
+ if (useColor) {
+ if (testCase.result == PASS) {
+ return "${GREEN_COLOR}${result}${NO_COLOR}";
+ } else if (testCase.result == FAIL) {
+ return "${RED_COLOR}${result}${NO_COLOR}";
+ } else if (testCase.result == ERROR) {
+ return "${MAGENTA_COLOR}${result}${NO_COLOR}";
+ }
+ }
+ return result;
+ }
+
void onDone(bool success) {
try {
super.onDone(success);
diff --git a/runtime/embedders/openglui/common/vm_glue.cc b/runtime/embedders/openglui/common/vm_glue.cc
index 4836c8a..4cb5ff8 100644
--- a/runtime/embedders/openglui/common/vm_glue.cc
+++ b/runtime/embedders/openglui/common/vm_glue.cc
@@ -143,6 +143,7 @@
NULL,
NULL,
NULL,
+ NULL,
NULL)) {
LOGE("VM initialization failed\n");
return -1;
diff --git a/runtime/lib/date_patch.dart b/runtime/lib/date_patch.dart
index b1f2f8d..f959723 100644
--- a/runtime/lib/date_patch.dart
+++ b/runtime/lib/date_patch.dart
@@ -5,6 +5,19 @@
// VM implementation of DateTime.
patch class DateTime {
+ // Natives.
+ // The natives have been moved up here to work around Issue 10401.
+ static int _getCurrentMs() native "DateNatives_currentTimeMillis";
+
+ static String _timeZoneNameForClampedSeconds(int secondsSinceEpoch)
+ native "DateNatives_timeZoneName";
+
+ static int _timeZoneOffsetInSecondsForClampedSeconds(int secondsSinceEpoch)
+ native "DateNatives_timeZoneOffsetInSeconds";
+
+ static int _localTimeZoneAdjustmentInSeconds()
+ native "DateNatives_localTimeZoneAdjustmentInSeconds";
+
/* patch */ DateTime._internal(int year,
int month,
int day,
@@ -310,16 +323,4 @@
int equivalentSeconds = _equivalentSeconds(millisecondsSinceEpoch);
return _timeZoneNameForClampedSeconds(equivalentSeconds);
}
-
- // Natives
- static int _getCurrentMs() native "DateNatives_currentTimeMillis";
-
- static String _timeZoneNameForClampedSeconds(int secondsSinceEpoch)
- native "DateNatives_timeZoneName";
-
- static int _timeZoneOffsetInSecondsForClampedSeconds(int secondsSinceEpoch)
- native "DateNatives_timeZoneOffsetInSeconds";
-
- static int _localTimeZoneAdjustmentInSeconds()
- native "DateNatives_localTimeZoneAdjustmentInSeconds";
}
diff --git a/runtime/lib/isolate_patch.dart b/runtime/lib/isolate_patch.dart
index a4daac2..2280aa9 100644
--- a/runtime/lib/isolate_patch.dart
+++ b/runtime/lib/isolate_patch.dart
@@ -136,7 +136,7 @@
}
Future call(var message) {
- final completer = new Completer();
+ final completer = new Completer.sync();
final port = new _ReceivePortImpl();
send(message, port.toSendPort());
port.receive((value, ignoreReplyTo) {
diff --git a/runtime/lib/mirrors_impl.dart b/runtime/lib/mirrors_impl.dart
index 614e5589..ef95c2b 100644
--- a/runtime/lib/mirrors_impl.dart
+++ b/runtime/lib/mirrors_impl.dart
@@ -199,36 +199,31 @@
var arg = positionalArguments[i];
_validateArgument(i, arg);
}
- Completer<InstanceMirror> completer = new Completer<InstanceMirror>();
try {
- completer.complete(
+ return new Future<InstanceMirror>.value(
_invoke(this, _n(memberName), positionalArguments, true));
} catch (exception, s) {
- completer.completeError(exception, s);
+ return new Future<InstanceMirror>.error(exception, s);
}
- return completer.future;
}
Future<InstanceMirror> getFieldAsync(Symbol fieldName) {
- Completer<InstanceMirror> completer = new Completer<InstanceMirror>();
try {
- completer.complete(_getField(this, _n(fieldName)));
+ return new Future<InstanceMirror>.value(_getField(this, _n(fieldName)));
} catch (exception, s) {
- completer.completeError(exception, s);
+ return new Future<InstanceMirror>.error(exception, s);
}
- return completer.future;
}
Future<InstanceMirror> setFieldAsync(Symbol fieldName, Object arg) {
_validateArgument(0, arg);
- Completer<InstanceMirror> completer = new Completer<InstanceMirror>();
try {
- completer.complete(_setField(this, _n(fieldName), arg, true));
+ return new Future<InstanceMirror>.value(
+ _setField(this, _n(fieldName), arg, true));
} catch (exception, s) {
- completer.completeError(exception, s);
+ return new Future<InstanceMirror>.error(exception, s);
}
- return completer.future;
}
static _validateArgument(int i, Object arg)
@@ -388,14 +383,12 @@
var arg = positionalArguments[i];
_LocalObjectMirrorImpl._validateArgument(i, arg);
}
- Completer<InstanceMirror> completer = new Completer<InstanceMirror>();
try {
- completer.complete(
+ return new Future<InstanceMirror>.value(
_apply(this, positionalArguments, true));
} catch (exception) {
- completer.completeError(exception);
+ return new Future<InstanceMirror>.error(exception);
}
- return completer.future;
}
Future<InstanceMirror> findInContext(Symbol name) {
@@ -598,17 +591,15 @@
var arg = positionalArguments[i];
_LocalObjectMirrorImpl._validateArgument(i, arg);
}
- Completer<InstanceMirror> completer = new Completer<InstanceMirror>();
try {
- completer.complete(
+ return new Future<InstanceMirror>.value(
_invokeConstructor(this,
_n(constructorName),
positionalArguments,
true));
} catch (exception) {
- completer.completeError(exception);
+ return new Future<InstanceMirror>.error(exception);
}
- return completer.future;
}
static _invokeConstructor(ref, constructorName, positionalArguments, async)
@@ -1046,20 +1037,18 @@
}
static Future<MirrorSystem> mirrorSystemOf(SendPort port) {
- Completer<MirrorSystem> completer = new Completer<MirrorSystem>();
if (isLocalPort(port)) {
// Make a local mirror system.
try {
- completer.complete(currentMirrorSystem());
+ return new Future<MirrorSystem>.value(currentMirrorSystem());
} catch (exception) {
- completer.completeError(exception);
+ return new Future<MirrorSystem>.error(exception);
}
} else {
// Make a remote mirror system
throw new UnimplementedError(
'Remote mirror support is not implemented');
}
- return completer.future;
}
// Creates a new local InstanceMirror
diff --git a/runtime/tools/gen_library_src_paths.py b/runtime/tools/gen_library_src_paths.py
index 2cb082b..2751215 100644
--- a/runtime/tools/gen_library_src_paths.py
+++ b/runtime/tools/gen_library_src_paths.py
@@ -38,6 +38,7 @@
os.path.basename(string_file).replace('\\', '\\\\') + '", ')
part_index.append('"' +
os.path.abspath(string_file).replace('\\', '\\\\') + '", \n')
+ bootstrap_cc_text = bootstrap_cc_text.replace("{{LIBRARY_SOURCE_MAP}}", '')
bootstrap_cc_text = bootstrap_cc_text.replace("{{PART_SOURCE_MAP}}",
''.join(part_index))
open(output_file, 'w').write(bootstrap_cc_text)
diff --git a/runtime/vm/assembler_arm.cc b/runtime/vm/assembler_arm.cc
index 5f74eeb..7a0f82f 100644
--- a/runtime/vm/assembler_arm.cc
+++ b/runtime/vm/assembler_arm.cc
@@ -5,6 +5,11 @@
#include "vm/globals.h"
#if defined(TARGET_ARCH_ARM)
+// An extra check since we are assuming the existence of /proc/cpuinfo below.
+#if !defined(USING_SIMULATOR) && !defined(__linux__)
+#error ARM cross-compile only supported on Linux
+#endif
+
#include "vm/assembler.h"
#include "vm/simulator.h"
#include "vm/runtime_entry.h"
@@ -27,6 +32,8 @@
}
+// If we are using the simulator, allow tests to enable/disable support for
+// integer division.
#if defined(USING_SIMULATOR)
void CPUFeatures::set_integer_division_supported(bool supported) {
integer_division_supported_ = supported;
@@ -34,21 +41,56 @@
#endif
-#define __ assembler.
+// Probe /proc/cpuinfo for features of the ARM processor.
+#if !defined(USING_SIMULATOR)
+static bool CPUInfoContainsString(const char* search_string) {
+ const char* file_name = "/proc/cpuinfo";
+ // This is written as a straight shot one pass parser
+ // and not using STL string and ifstream because,
+ // on Linux, it's reading from a (non-mmap-able)
+ // character special device.
+ FILE* f = NULL;
+ const char* what = search_string;
+
+ if (NULL == (f = fopen(file_name, "r")))
+ return false;
+
+ int k;
+ while (EOF != (k = fgetc(f))) {
+ if (k == *what) {
+ ++what;
+ while ((*what != '\0') && (*what == fgetc(f))) {
+ ++what;
+ }
+ if (*what == '\0') {
+ fclose(f);
+ return true;
+ } else {
+ what = search_string;
+ }
+ }
+ }
+ fclose(f);
+
+ // Did not find string in the proc file.
+ return false;
+}
+#endif
void CPUFeatures::InitOnce() {
#if defined(USING_SIMULATOR)
integer_division_supported_ = true;
#else
- integer_division_supported_ = false;
+ ASSERT(CPUInfoContainsString("ARMv7")); // Implements ARMv7.
+ ASSERT(CPUInfoContainsString("vfp")); // Has floating point unit.
+ // Has integer division.
+ integer_division_supported_ = CPUInfoContainsString("idiva");
#endif // defined(USING_SIMULATOR)
#if defined(DEBUG)
initialized_ = true;
#endif
}
-#undef __
-
// Instruction encoding bits.
enum {
@@ -89,13 +131,6 @@
B25 = 1 << 25,
B26 = 1 << 26,
B27 = 1 << 27,
-
- // ldrex/strex register field encodings.
- kLdExRnShift = 16,
- kLdExRtShift = 12,
- kStrExRnShift = 16,
- kStrExRdShift = 12,
- kStrExRtShift = 0,
};
@@ -491,10 +526,10 @@
ASSERT(cond != kNoCondition);
int32_t encoding = opcode |
(static_cast<int32_t>(cond) << kConditionShift) |
- (static_cast<int32_t>(rn) << kRnShift) |
- (static_cast<int32_t>(rd) << kRdShift) |
+ (static_cast<int32_t>(rn) << kDivRnShift) |
+ (static_cast<int32_t>(rd) << kDivRdShift) |
B26 | B25 | B24 | B20 | B4 |
- (static_cast<int32_t>(rm) << kRmShift);
+ (static_cast<int32_t>(rm) << kDivRmShift);
Emit(encoding);
}
diff --git a/runtime/vm/assembler_arm.h b/runtime/vm/assembler_arm.h
index fa2744d..6195664 100644
--- a/runtime/vm/assembler_arm.h
+++ b/runtime/vm/assembler_arm.h
@@ -660,7 +660,7 @@
// Instruction pattern from entrypoint is used in Dart frame prologs
// to set up the frame and save a PC which can be used to figure out the
// RawInstruction object corresponding to the code running in the frame.
- static const intptr_t kOffsetOfSavedPCfromEntrypoint = Instr::kPCReadOffset;
+ static const intptr_t kEntryPointToPcMarkerOffset = Instr::kPCReadOffset;
// Inlined allocation of an instance of class 'cls', code has no runtime
// calls. Jump to 'failure' if the instance cannot be allocated here.
diff --git a/runtime/vm/assembler_arm_test.cc b/runtime/vm/assembler_arm_test.cc
index 7a18568..52de670 100644
--- a/runtime/vm/assembler_arm_test.cc
+++ b/runtime/vm/assembler_arm_test.cc
@@ -1389,7 +1389,10 @@
// are in agreement.
#if defined(USING_SIMULATOR)
ASSEMBLER_TEST_GENERATE(MrcHaveDiv, assembler) {
- __ mrc(R0, 15, 0, 0, 2, 0);
+ __ mrc(R0, 15, 0, 0, 2, 0); // Accesses ID_ISAR0.
+ // Bits 24 - 27 describe the presence of integer division. Bit 24 is set if
+ // it is available in the Thumb instruction set. Bit 25 is set if it is
+ // available both in Thumb and in the ARM instruction set.
__ Lsr(R0, R0, 24);
__ and_(R0, R0, ShifterOperand(0xf));
__ mov(PC, ShifterOperand(LR));
@@ -1401,7 +1404,7 @@
typedef int (*Tst)();
bool b = CPUFeatures::integer_division_supported();
CPUFeatures::set_integer_division_supported(true);
- EXPECT_LT(0, EXECUTE_TEST_CODE_INT32(Tst, test->entry()));
+ EXPECT_EQ(2, EXECUTE_TEST_CODE_INT32(Tst, test->entry()));
CPUFeatures::set_integer_division_supported(b);
}
@@ -1422,7 +1425,6 @@
EXPECT_EQ(0, EXECUTE_TEST_CODE_INT32(Tst, test->entry()));
CPUFeatures::set_integer_division_supported(b);
}
-#endif // defined(USING_SIMULATOR)
ASSEMBLER_TEST_GENERATE(MrcReal, assembler) {
@@ -1439,18 +1441,23 @@
bool have_div = CPUFeatures::integer_division_supported();
int32_t r = EXECUTE_TEST_CODE_INT32(Tst, test->entry());
if (have_div) {
- EXPECT_LT(0, r);
+ EXPECT_EQ(2, r);
} else {
EXPECT_EQ(0, r);
}
}
+#endif // defined(USING_SIMULATOR)
ASSEMBLER_TEST_GENERATE(Udiv, assembler) {
- __ mov(R0, ShifterOperand(27));
- __ mov(R1, ShifterOperand(9));
- __ udiv(R2, R0, R1);
- __ Mov(R0, R2);
+ if (CPUFeatures::integer_division_supported()) {
+ __ mov(R0, ShifterOperand(27));
+ __ mov(R1, ShifterOperand(9));
+ __ udiv(R2, R0, R1);
+ __ Mov(R0, R2);
+ } else {
+ __ mov(R0, ShifterOperand(3));
+ }
__ mov(PC, ShifterOperand(LR));
}
@@ -1463,10 +1470,14 @@
ASSEMBLER_TEST_GENERATE(Sdiv, assembler) {
- __ mov(R0, ShifterOperand(27));
- __ LoadImmediate(R1, -9);
- __ sdiv(R2, R0, R1);
- __ Mov(R0, R2);
+ if (CPUFeatures::integer_division_supported()) {
+ __ mov(R0, ShifterOperand(27));
+ __ LoadImmediate(R1, -9);
+ __ sdiv(R2, R0, R1);
+ __ Mov(R0, R2);
+ } else {
+ __ LoadImmediate(R0, -3);
+ }
__ mov(PC, ShifterOperand(LR));
}
@@ -1479,10 +1490,14 @@
ASSEMBLER_TEST_GENERATE(Udiv_zero, assembler) {
- __ mov(R0, ShifterOperand(27));
- __ mov(R1, ShifterOperand(0));
- __ udiv(R2, R0, R1);
- __ Mov(R0, R2);
+ if (CPUFeatures::integer_division_supported()) {
+ __ mov(R0, ShifterOperand(27));
+ __ mov(R1, ShifterOperand(0));
+ __ udiv(R2, R0, R1);
+ __ Mov(R0, R2);
+ } else {
+ __ LoadImmediate(R0, 0);
+ }
__ mov(PC, ShifterOperand(LR));
}
@@ -1495,10 +1510,14 @@
ASSEMBLER_TEST_GENERATE(Sdiv_zero, assembler) {
- __ mov(R0, ShifterOperand(27));
- __ mov(R1, ShifterOperand(0));
- __ udiv(R2, R0, R1);
- __ Mov(R0, R2);
+ if (CPUFeatures::integer_division_supported()) {
+ __ mov(R0, ShifterOperand(27));
+ __ mov(R1, ShifterOperand(0));
+ __ udiv(R2, R0, R1);
+ __ Mov(R0, R2);
+ } else {
+ __ LoadImmediate(R0, 0);
+ }
__ mov(PC, ShifterOperand(LR));
}
@@ -1511,10 +1530,14 @@
ASSEMBLER_TEST_GENERATE(Udiv_corner, assembler) {
- __ LoadImmediate(R0, 0x80000000);
- __ LoadImmediate(R1, 0xffffffff);
- __ udiv(R2, R0, R1);
- __ Mov(R0, R2);
+ if (CPUFeatures::integer_division_supported()) {
+ __ LoadImmediate(R0, 0x80000000);
+ __ LoadImmediate(R1, 0xffffffff);
+ __ udiv(R2, R0, R1);
+ __ Mov(R0, R2);
+ } else {
+ __ LoadImmediate(R0, 0);
+ }
__ mov(PC, ShifterOperand(LR));
}
@@ -1527,10 +1550,14 @@
ASSEMBLER_TEST_GENERATE(Sdiv_corner, assembler) {
- __ LoadImmediate(R0, 0x80000000);
- __ LoadImmediate(R1, 0xffffffff);
- __ sdiv(R2, R0, R1);
- __ Mov(R0, R2);
+ if (CPUFeatures::integer_division_supported()) {
+ __ LoadImmediate(R0, 0x80000000);
+ __ LoadImmediate(R1, 0xffffffff);
+ __ sdiv(R2, R0, R1);
+ __ Mov(R0, R2);
+ } else {
+ __ LoadImmediate(R0, 0x80000000);
+ }
__ mov(PC, ShifterOperand(LR));
}
diff --git a/runtime/vm/assembler_ia32.h b/runtime/vm/assembler_ia32.h
index d28f814..56891a5 100644
--- a/runtime/vm/assembler_ia32.h
+++ b/runtime/vm/assembler_ia32.h
@@ -726,7 +726,7 @@
// movl ebp, esp (size is 2 bytes)
// call L (size is 5 bytes)
// L:
- static const intptr_t kOffsetOfSavedPCfromEntrypoint = 8;
+ static const intptr_t kEntryPointToPcMarkerOffset = 8;
// Inlined allocation of an instance of class 'cls', code has no runtime
// calls. Jump to 'failure' if the instance cannot be allocated here.
diff --git a/runtime/vm/assembler_mips.h b/runtime/vm/assembler_mips.h
index 07d19bc..5604c26 100644
--- a/runtime/vm/assembler_mips.h
+++ b/runtime/vm/assembler_mips.h
@@ -179,7 +179,7 @@
// to set up the frame and save a PC which can be used to figure out the
// RawInstruction object corresponding to the code running in the frame.
// See EnterDartFrame. There are 6 instructions before we know the PC.
- static const intptr_t kOffsetOfSavedPCfromEntrypoint = 6 * Instr::kInstrSize;
+ static const intptr_t kEntryPointToPcMarkerOffset = 6 * Instr::kInstrSize;
// Inlined allocation of an instance of class 'cls', code has no runtime
// calls. Jump to 'failure' if the instance cannot be allocated here.
diff --git a/runtime/vm/assembler_x64.h b/runtime/vm/assembler_x64.h
index 9ba4fba..7572d0e 100644
--- a/runtime/vm/assembler_x64.h
+++ b/runtime/vm/assembler_x64.h
@@ -749,7 +749,7 @@
// movq rbp, rsp (size is 3 bytes)
// call L (size is 5 bytes)
// L:
- static const intptr_t kOffsetOfSavedPCfromEntrypoint = 9;
+ static const intptr_t kEntryPointToPcMarkerOffset = 9;
// Inlined allocation of an instance of class 'cls', code has no runtime
// calls. Jump to 'failure' if the instance cannot be allocated here.
diff --git a/runtime/vm/benchmark_test.h b/runtime/vm/benchmark_test.h
index ce7f2d0..6b3fcf9 100644
--- a/runtime/vm/benchmark_test.h
+++ b/runtime/vm/benchmark_test.h
@@ -23,7 +23,7 @@
// of the VM
#define BENCHMARK(name) \
void Dart_Benchmark##name(Benchmark* benchmark); \
- static const Benchmark kRegister##name(Dart_Benchmark##name, #name); \
+ static Benchmark kRegister##name(Dart_Benchmark##name, #name); \
static void Dart_BenchmarkHelper##name(Benchmark* benchmark); \
void Dart_Benchmark##name(Benchmark* benchmark) { \
FLAG_heap_growth_space_ratio = 100; \
diff --git a/runtime/vm/bootstrap.cc b/runtime/vm/bootstrap.cc
index 01008aa..e95dc90 100644
--- a/runtime/vm/bootstrap.cc
+++ b/runtime/vm/bootstrap.cc
@@ -27,7 +27,7 @@
const char* uri_;
const char** source_paths_;
const char* patch_uri_;
- const char* patch_source_;
+ const char** patch_paths_;
} bootstrap_lib_props;
@@ -35,19 +35,19 @@
INIT_LIBRARY(ObjectStore::kCore,
core,
Bootstrap::corelib_source_paths_,
- Bootstrap::corelib_patch_),
+ Bootstrap::corelib_patch_paths_),
INIT_LIBRARY(ObjectStore::kAsync,
async,
Bootstrap::async_source_paths_,
- Bootstrap::async_patch_),
+ Bootstrap::async_patch_paths_),
INIT_LIBRARY(ObjectStore::kCollection,
collection,
Bootstrap::collection_source_paths_,
- Bootstrap::collection_patch_),
+ Bootstrap::collection_patch_paths_),
INIT_LIBRARY(ObjectStore::kCollectionDev,
_collection-dev,
Bootstrap::collection_dev_source_paths_,
- Bootstrap::collection_dev_patch_),
+ Bootstrap::collection_dev_patch_paths_),
INIT_LIBRARY(ObjectStore::kCrypto,
crypto,
Bootstrap::crypto_source_paths_,
@@ -55,23 +55,23 @@
INIT_LIBRARY(ObjectStore::kIsolate,
isolate,
Bootstrap::isolate_source_paths_,
- Bootstrap::isolate_patch_),
+ Bootstrap::isolate_patch_paths_),
INIT_LIBRARY(ObjectStore::kJson,
json,
Bootstrap::json_source_paths_,
- Bootstrap::json_patch_),
+ Bootstrap::json_patch_paths_),
INIT_LIBRARY(ObjectStore::kMath,
math,
Bootstrap::math_source_paths_,
- Bootstrap::math_patch_),
+ Bootstrap::math_patch_paths_),
INIT_LIBRARY(ObjectStore::kMirrors,
mirrors,
Bootstrap::mirrors_source_paths_,
- Bootstrap::mirrors_patch_),
+ Bootstrap::mirrors_patch_paths_),
INIT_LIBRARY(ObjectStore::kTypedData,
typed_data,
Bootstrap::typed_data_source_paths_,
- Bootstrap::typed_data_patch_),
+ Bootstrap::typed_data_patch_paths_),
INIT_LIBRARY(ObjectStore::kUtf,
utf,
Bootstrap::utf_source_paths_,
@@ -90,38 +90,32 @@
bool patch) {
// First check if this is a valid boot strap library and find it's index
// in the 'bootstrap_libraries' table above.
- intptr_t index = 0;
+ intptr_t index;
const String& lib_uri = String::Handle(lib.url());
- while (bootstrap_libraries[index].index_ != ObjectStore::kNone) {
+ for (index = 0;
+ bootstrap_libraries[index].index_ != ObjectStore::kNone;
+ ++index) {
if (lib_uri.Equals(bootstrap_libraries[index].uri_)) {
break;
}
- index += 1;
}
if (bootstrap_libraries[index].index_ == ObjectStore::kNone) {
return String::null(); // Library is not a boot strap library.
}
- if (patch) {
- // TODO(asiva): Replace with actual read of the source file.
- const char* source = bootstrap_libraries[index].patch_source_;
- ASSERT(source != NULL);
- return String::New(source, Heap::kOld);
- }
-
// Try to read the source using the path specified for the uri.
- const char** source_paths = bootstrap_libraries[index].source_paths_;
+ const char** source_paths = patch ?
+ bootstrap_libraries[index].patch_paths_ :
+ bootstrap_libraries[index].source_paths_;
if (source_paths == NULL) {
return String::null(); // No path mapping information exists for library.
}
- intptr_t i = 0;
const char* source_path = NULL;
- while (source_paths[i] != NULL) {
+ for (intptr_t i = 0; source_paths[i] != NULL; i += 2) {
if (uri.Equals(source_paths[i])) {
source_path = source_paths[i + 1];
break;
}
- i += 2;
}
if (source_path == NULL) {
return String::null(); // Uri does not exist in path mapping information.
@@ -234,6 +228,39 @@
}
+static RawError* LoadPatchFiles(Isolate* isolate,
+ const Library& lib,
+ const String& patch_uri,
+ const char** patch_files) {
+ String& patch_file_uri = String::Handle(isolate);
+ String& source = String::Handle(isolate);
+ Script& script = Script::Handle(isolate);
+ Error& error = Error::Handle(isolate);
+ const Array& strings = Array::Handle(isolate, Array::New(3));
+ strings.SetAt(0, patch_uri);
+ strings.SetAt(1, Symbols::Slash());
+ for (intptr_t j = 0; patch_files[j] != NULL; j += 2) {
+ patch_file_uri = String::New(patch_files[j]);
+ source = GetLibrarySource(lib, patch_file_uri, true);
+ if (source.IsNull()) {
+ return Api::UnwrapErrorHandle(
+ isolate,
+ Api::NewError("Unable to find dart patch source for %s",
+ patch_file_uri.ToCString())).raw();
+ }
+ // Prepend the patch library URI to form a unique script URI for the patch.
+ strings.SetAt(2, patch_file_uri);
+ patch_file_uri = String::ConcatAll(strings);
+ script = Script::New(patch_file_uri, source, RawScript::kPatchTag);
+ error = lib.Patch(script);
+ if (!error.IsNull()) {
+ return error.raw();
+ }
+ }
+ return Error::null();
+}
+
+
RawError* Bootstrap::LoadandCompileScripts() {
Isolate* isolate = Isolate::Current();
String& uri = String::Handle();
@@ -253,8 +280,9 @@
Dart_EnterScope();
// Create library objects for all the bootstrap libraries.
- intptr_t i = 0;
- while (bootstrap_libraries[i].index_ != ObjectStore::kNone) {
+ for (intptr_t i = 0;
+ bootstrap_libraries[i].index_ != ObjectStore::kNone;
+ ++i) {
uri = Symbols::New(bootstrap_libraries[i].uri_);
lib = Library::LookupLibrary(uri);
if (lib.IsNull()) {
@@ -263,12 +291,12 @@
}
isolate->object_store()->set_bootstrap_library(
bootstrap_libraries[i].index_, lib);
- i = i + 1;
}
- // Load and compile bootstrap libraries.
- i = 0;
- while (bootstrap_libraries[i].index_ != ObjectStore::kNone) {
+ // Load, compile and patch bootstrap libraries.
+ for (intptr_t i = 0;
+ bootstrap_libraries[i].index_ != ObjectStore::kNone;
+ ++i) {
uri = Symbols::New(bootstrap_libraries[i].uri_);
lib = Library::LookupLibrary(uri);
ASSERT(!lib.IsNull());
@@ -285,23 +313,16 @@
break;
}
// If a patch exists, load and patch the script.
- if (bootstrap_libraries[i].patch_source_ != NULL) {
- patch_uri = String::New(bootstrap_libraries[i].patch_uri_,
- Heap::kOld);
- source = GetLibrarySource(lib, uri, true);
- if (source.IsNull()) {
- error ^= Api::UnwrapErrorHandle(
- isolate, Api::NewError("Unable to find dart patch source for %s",
- uri.ToCString())).raw();
- break;
- }
- script = Script::New(patch_uri, source, RawScript::kPatchTag);
- error = lib.Patch(script);
+ if (bootstrap_libraries[i].patch_paths_ != NULL) {
+ patch_uri = Symbols::New(bootstrap_libraries[i].patch_uri_);
+ error = LoadPatchFiles(isolate,
+ lib,
+ patch_uri,
+ bootstrap_libraries[i].patch_paths_);
if (!error.IsNull()) {
break;
}
}
- i = i + 1;
}
if (error.IsNull()) {
SetupNativeResolver();
diff --git a/runtime/vm/bootstrap.h b/runtime/vm/bootstrap.h
index 8c3ae55..d873541 100644
--- a/runtime/vm/bootstrap.h
+++ b/runtime/vm/bootstrap.h
@@ -31,16 +31,16 @@
static const char* uri_source_paths_[];
static const char* utf_source_paths_[];
- // Patch sources for libaries (concatenated source).
- static const char async_patch_[];
- static const char corelib_patch_[];
- static const char collection_patch_[];
- static const char collection_dev_patch_[];
- static const char isolate_patch_[];
- static const char json_patch_[];
- static const char math_patch_[];
- static const char mirrors_patch_[];
- static const char typed_data_patch_[];
+ // Source path mapping for patch URI and 'parts'.
+ static const char* async_patch_paths_[];
+ static const char* corelib_patch_paths_[];
+ static const char* collection_patch_paths_[];
+ static const char* collection_dev_patch_paths_[];
+ static const char* isolate_patch_paths_[];
+ static const char* json_patch_paths_[];
+ static const char* math_patch_paths_[];
+ static const char* mirrors_patch_paths_[];
+ static const char* typed_data_patch_paths_[];
};
} // namespace dart
diff --git a/runtime/vm/code_generator.cc b/runtime/vm/code_generator.cc
index fb9e846..d4a4ce2 100644
--- a/runtime/vm/code_generator.cc
+++ b/runtime/vm/code_generator.cc
@@ -1482,15 +1482,16 @@
// FP, PC-marker and return-address will be copied as well.
const intptr_t frame_copy_size =
// Deoptimized function's return address: caller_frame->pc().
- - kPcSlotIndexFromSp
+ - kSavedPcSlotFromSp
+ ((frame.fp() - frame.sp()) / kWordSize)
- + kLastParamSlotIndex
+ + 1 // For fp.
+ + kParamEndSlotFromFp
+ num_args;
intptr_t* frame_copy = new intptr_t[frame_copy_size];
ASSERT(frame_copy != NULL);
// Include the return address of optimized code.
intptr_t* start = reinterpret_cast<intptr_t*>(
- frame.sp() + (kPcSlotIndexFromSp * kWordSize));
+ frame.sp() + (kSavedPcSlotFromSp * kWordSize));
for (intptr_t i = 0; i < frame_copy_size; i++) {
frame_copy[i] = *(start + i);
}
@@ -1545,8 +1546,10 @@
const intptr_t num_args =
function.HasOptionalParameters() ? 0 : function.num_fixed_parameters();
intptr_t unoptimized_stack_size =
- + deopt_info.TranslationLength() - num_args
- - kLastParamSlotIndex; // Subtract caller FP and PC (possibly pc marker).
+ + deopt_info.FrameSize()
+ - num_args
+ - kParamEndSlotFromFp
+ - 1; // For fp.
return unoptimized_stack_size * kWordSize;
}
END_LEAF_RUNTIME_ENTRY
@@ -1563,30 +1566,59 @@
deopt_info.ToInstructions(deopt_table, &deopt_instructions);
intptr_t* start = reinterpret_cast<intptr_t*>(
- caller_frame.sp() + (kPcSlotIndexFromSp * kWordSize));
+ caller_frame.sp() + (kSavedPcSlotFromSp * kWordSize));
const Function& function = Function::Handle(code.function());
const intptr_t num_args =
function.HasOptionalParameters() ? 0 : function.num_fixed_parameters();
const intptr_t to_frame_size =
- - kPcSlotIndexFromSp // Deoptimized function's return address.
+ - kSavedPcSlotFromSp // Deoptimized function's return address.
+ (caller_frame.fp() - caller_frame.sp()) / kWordSize
- + kLastParamSlotIndex
+ + 1 // For fp.
+ + kParamEndSlotFromFp
+ num_args;
DeoptimizationContext deopt_context(start,
to_frame_size,
Array::Handle(code.object_table()),
num_args,
static_cast<DeoptReasonId>(deopt_reason));
- for (intptr_t to_index = len - 1; to_index >= 0; to_index--) {
- deopt_instructions[to_index]->Execute(&deopt_context, to_index);
+ const intptr_t frame_size = deopt_info.FrameSize();
+
+ // All kMaterializeObject instructions are emitted before the instructions
+ // that describe stack frames. Skip them and defer materialization of
+ // objects until the frame is fully reconstructed and it is safe to perform
+ // GC.
+ // Arguments (class of the instance to allocate and field-value pairs) are
+ // described as part of the expression stack for the bottom-most deoptimized
+ // frame. They will be used during materialization and removed from the stack
+ // right before control switches to the unoptimized code.
+ const intptr_t num_materializations = len - frame_size;
+ Isolate::Current()->PrepareForDeferredMaterialization(num_materializations);
+ for (intptr_t from_index = 0, to_index = 1;
+ from_index < num_materializations;
+ from_index++) {
+ const intptr_t field_count =
+ DeoptInstr::GetFieldCount(deopt_instructions[from_index]);
+ intptr_t* args = deopt_context.GetToFrameAddressAt(to_index);
+ DeferredObject* obj = new DeferredObject(field_count, args);
+ Isolate::Current()->SetDeferredObjectAt(from_index, obj);
+ to_index += obj->ArgumentCount();
}
+
+ // Populate stack frames.
+ for (intptr_t to_index = frame_size - 1, from_index = len - 1;
+ to_index >= 0;
+ to_index--, from_index--) {
+ intptr_t* to_addr = deopt_context.GetToFrameAddressAt(to_index);
+ deopt_instructions[from_index]->Execute(&deopt_context, to_addr);
+ }
+
if (FLAG_trace_deoptimization_verbose) {
- for (intptr_t i = 0; i < len; i++) {
- OS::PrintErr("*%"Pd". [%p] %#014"Px" [%s]\n",
- i,
- &start[i],
- start[i],
- deopt_instructions[i]->ToCString());
+ for (intptr_t i = 0; i < frame_size; i++) {
+ OS::PrintErr("*%"Pd". [%"Px"] %#014"Px" [%s]\n",
+ i,
+ reinterpret_cast<uword>(&start[i]),
+ start[i],
+ deopt_instructions[i + (len - frame_size)]->ToCString());
}
}
return deopt_context.GetCallerFp();
@@ -1637,17 +1669,30 @@
// This is the last step in the deoptimization, GC can occur.
-DEFINE_RUNTIME_ENTRY(DeoptimizeMaterializeDoubles, 0) {
- DeferredObject* deferred_object = Isolate::Current()->DetachDeferredObjects();
+// Returns number of bytes to remove from the expression stack of the
+// bottom-most deoptimized frame. Those arguments were artificially injected
+// under return address to keep them discoverable by GC that can occur during
+// materialization phase.
+DEFINE_RUNTIME_ENTRY(DeoptimizeMaterialize, 0) {
+ // First materialize all unboxed "primitive" values (doubles, mints, simd)
+ // then materialize objects. The order is important: objects might be
+ // referencing boxes allocated on the first step. At the same time
+ // objects can't be referencing other deferred objects because storing
+ // an object into a field is always conservatively treated as escaping by
+ // allocation sinking and load forwarding.
+ isolate->MaterializeDeferredBoxes();
+ isolate->MaterializeDeferredObjects();
- while (deferred_object != NULL) {
- DeferredObject* current = deferred_object;
- deferred_object = deferred_object->next();
-
- current->Materialize();
-
- delete current;
+ // Compute total number of artificial arguments used during deoptimization.
+ intptr_t deopt_arguments = 0;
+ for (intptr_t i = 0; i < isolate->DeferredObjectsCount(); i++) {
+ deopt_arguments += isolate->GetDeferredObject(i)->ArgumentCount();
}
+ Isolate::Current()->DeleteDeferredObjects();
+
+ // Return value tells deoptimization stub to remove the given number of bytes
+ // from the stack.
+ arguments.SetReturn(Smi::Handle(Smi::New(deopt_arguments * kWordSize)));
// Since this is the only step where GC can occur during deoptimization,
// use it to report the source line where deoptimization occured.
diff --git a/runtime/vm/code_generator.h b/runtime/vm/code_generator.h
index 8b0a59c..dcea908 100644
--- a/runtime/vm/code_generator.h
+++ b/runtime/vm/code_generator.h
@@ -47,7 +47,7 @@
DECLARE_RUNTIME_ENTRY(Throw);
DECLARE_RUNTIME_ENTRY(TraceFunctionEntry);
DECLARE_RUNTIME_ENTRY(TraceFunctionExit);
-DECLARE_RUNTIME_ENTRY(DeoptimizeMaterializeDoubles);
+DECLARE_RUNTIME_ENTRY(DeoptimizeMaterialize);
DECLARE_RUNTIME_ENTRY(UpdateICDataTwoArgs);
DECLARE_RUNTIME_ENTRY(UpdateFieldCid);
diff --git a/runtime/vm/compiler.cc b/runtime/vm/compiler.cc
index 60af119..a16d853 100644
--- a/runtime/vm/compiler.cc
+++ b/runtime/vm/compiler.cc
@@ -45,6 +45,8 @@
DEFINE_FLAG(bool, loop_invariant_code_motion, true,
"Do loop invariant code motion.");
DEFINE_FLAG(bool, propagate_types, true, "Do static type propagation.");
+DEFINE_FLAG(bool, allocation_sinking, true,
+ "attempt to sink temporary allocations to side exits");
DEFINE_FLAG(int, deoptimization_counter_threshold, 16,
"How many times we allow deoptimization before we disallow optimization.");
DEFINE_FLAG(bool, use_inlining, true, "Enable call-site inlining");
@@ -267,6 +269,7 @@
licm.Optimize();
DEBUG_ASSERT(flow_graph->VerifyUseLists());
}
+ flow_graph->RemoveRedefinitions();
if (FLAG_range_analysis) {
// We have to perform range analysis after LICM because it
@@ -291,6 +294,20 @@
propagator.Propagate();
DEBUG_ASSERT(flow_graph->VerifyUseLists());
}
+
+ // Detach environments from the instructions that can't deoptimize.
+ // Do it before we attempt to perform allocation sinking to minimize
+ // amount of materializations it has to perform.
+ optimizer.EliminateEnvironments();
+
+ // Attempt to sink allocations of temporary non-escaping objects to
+ // the deoptimization path.
+ AllocationSinking* sinking = NULL;
+ if (FLAG_allocation_sinking) {
+ sinking = new AllocationSinking(flow_graph);
+ sinking->Optimize();
+ }
+
if (optimizer.Canonicalize()) {
// To fully remove redundant boxing (e.g. BoxDouble used only in
// environments and UnboxDouble instructions) instruction we
@@ -301,6 +318,14 @@
}
DEBUG_ASSERT(flow_graph->VerifyUseLists());
+ if (sinking != NULL) {
+ // Remove all MaterializeObject instructions inserted by allocation
+ // sinking from the flow graph and let them float on the side referenced
+ // only from environments. Register allocator will consider them
+ // as part of a deoptimization environment.
+ sinking->DetachMaterializations();
+ }
+
// Perform register allocation on the SSA graph.
FlowGraphAllocator allocator(*flow_graph);
allocator.AllocateRegisters();
diff --git a/runtime/vm/constants_arm.h b/runtime/vm/constants_arm.h
index 6dc3034..8caf86d 100644
--- a/runtime/vm/constants_arm.h
+++ b/runtime/vm/constants_arm.h
@@ -293,12 +293,27 @@
kOffset12Bits = 12,
kOffset12Mask = 0x00000fff,
- // Mul instruction register fields encodings.
+ // Mul instruction register field encodings.
kMulRdShift = 16,
kMulRdBits = 4,
kMulRnShift = 12,
kMulRnBits = 4,
+ // Div instruction register field encodings.
+ kDivRdShift = 16,
+ kDivRdBits = 4,
+ kDivRmShift = 8,
+ kDivRmBits = 4,
+ kDivRnShift = 0,
+ kDivRnBits = 4,
+
+ // ldrex/strex register field encodings.
+ kLdExRnShift = 16,
+ kLdExRtShift = 12,
+ kStrExRnShift = 16,
+ kStrExRdShift = 12,
+ kStrExRtShift = 0,
+
// MRC instruction offset field encoding.
kCRmShift = 0,
kCRmBits = 4,
@@ -444,6 +459,16 @@
return bit_cast<double, uint64_t>(imm64);
}
+ inline Register DivRdField() const {
+ return static_cast<Register>(Bits(kDivRdShift, kDivRdBits));
+ }
+ inline Register DivRmField() const {
+ return static_cast<Register>(Bits(kDivRmShift, kDivRmBits));
+ }
+ inline Register DivRnField() const {
+ return static_cast<Register>(Bits(kDivRnShift, kDivRnBits));
+ }
+
// Test for data processing instructions of type 0 or 1.
// See "ARM Architecture Reference Manual ARMv7-A and ARMv7-R edition",
// section A5.1 "ARM instruction set encoding".
diff --git a/runtime/vm/cpu_arm.cc b/runtime/vm/cpu_arm.cc
index db8bb6f..dbf0407 100644
--- a/runtime/vm/cpu_arm.cc
+++ b/runtime/vm/cpu_arm.cc
@@ -32,7 +32,11 @@
const char* CPU::Id() {
- return "arm";
+ return
+#if !defined(HOST_ARCH_ARM)
+ "sim"
+#endif // !defined(HOST_ARCH_ARM)
+ "arm";
}
} // namespace dart
diff --git a/runtime/vm/cpu_mips.cc b/runtime/vm/cpu_mips.cc
index 51a679b..095ec4c 100644
--- a/runtime/vm/cpu_mips.cc
+++ b/runtime/vm/cpu_mips.cc
@@ -30,7 +30,11 @@
const char* CPU::Id() {
- return "mips";
+ return
+#if !defined(HOST_ARCH_MIPS)
+ "sim"
+#endif // !defined(HOST_ARCH_MIPS)
+ "mips";
}
} // namespace dart
diff --git a/runtime/vm/cpu_test.cc b/runtime/vm/cpu_test.cc
index 6f129fe..99e0b23 100644
--- a/runtime/vm/cpu_test.cc
+++ b/runtime/vm/cpu_test.cc
@@ -15,9 +15,17 @@
#elif defined(TARGET_ARCH_X64)
EXPECT_STREQ("x64", CPU::Id());
#elif defined(TARGET_ARCH_ARM)
+#if defined(HOST_ARCH_ARM)
EXPECT_STREQ("arm", CPU::Id());
+#else // defined(HOST_ARCH_ARM)
+ EXPECT_STREQ("simarm", CPU::Id());
+#endif // defined(HOST_ARCH_ARM)
#elif defined(TARGET_ARCH_MIPS)
+#if defined(HOST_ARCH_MIPS)
EXPECT_STREQ("mips", CPU::Id());
+#else // defined(HOST_ARCH_MIPS)
+ EXPECT_STREQ("simmips", CPU::Id());
+#endif // defined(HOST_ARCH_MIPS)
#else
#error Architecture was not detected as supported by Dart.
#endif
diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc
index 2757b83..db0b878 100644
--- a/runtime/vm/dart_api_impl.cc
+++ b/runtime/vm/dart_api_impl.cc
@@ -3813,16 +3813,16 @@
function_name,
(number_of_arguments + 1),
Resolver::kIsQualified));
- // TODO(5415268): Invoke noSuchMethod instead of failing.
- if (function.IsNull()) {
- const Type& type = Type::Handle(isolate, instance.GetType());
- const String& cls_name = String::Handle(isolate, type.ClassName());
- return Api::NewError("%s: did not find instance method '%s.%s'.",
- CURRENT_FUNC,
- cls_name.ToCString(),
- function_name.ToCString());
- }
args.SetAt(0, instance);
+ if (function.IsNull()) {
+ const Array& args_descriptor =
+ Array::Handle(ArgumentsDescriptor::New(args.Length()));
+ return Api::NewHandle(isolate,
+ DartEntry::InvokeNoSuchMethod(instance,
+ function_name,
+ args,
+ args_descriptor));
+ }
return Api::NewHandle(isolate, DartEntry::InvokeFunction(function, args));
} else if (obj.IsClass()) {
@@ -3933,9 +3933,9 @@
// field.
const Instance& instance = Instance::Cast(obj);
Class& cls = Class::Handle(isolate, instance.clazz());
+ String& getter_name =
+ String::Handle(isolate, Field::GetterName(field_name));
while (!cls.IsNull()) {
- String& getter_name =
- String::Handle(isolate, Field::GetterName(field_name));
getter = cls.LookupDynamicFunctionAllowPrivate(getter_name);
if (!getter.IsNull()) {
break;
@@ -3943,15 +3943,19 @@
cls = cls.SuperClass();
}
- if (getter.IsNull()) {
- return Api::NewError("%s: did not find instance field '%s'.",
- CURRENT_FUNC, field_name.ToCString());
- }
-
// Invoke the getter and return the result.
const int kNumArgs = 1;
const Array& args = Array::Handle(isolate, Array::New(kNumArgs));
args.SetAt(0, instance);
+ if (getter.IsNull()) {
+ const Array& args_descriptor =
+ Array::Handle(ArgumentsDescriptor::New(args.Length()));
+ return Api::NewHandle(isolate,
+ DartEntry::InvokeNoSuchMethod(instance,
+ getter_name,
+ args,
+ args_descriptor));
+ }
return Api::NewHandle(isolate, DartEntry::InvokeFunction(getter, args));
} else if (obj.IsClass()) {
@@ -4055,14 +4059,14 @@
// field.
const Instance& instance = Instance::Cast(obj);
Class& cls = Class::Handle(isolate, instance.clazz());
+ String& setter_name =
+ String::Handle(isolate, Field::SetterName(field_name));
while (!cls.IsNull()) {
field = cls.LookupInstanceField(field_name);
if (!field.IsNull() && field.is_final()) {
return Api::NewError("%s: cannot set final field '%s'.",
CURRENT_FUNC, field_name.ToCString());
}
- String& setter_name =
- String::Handle(isolate, Field::SetterName(field_name));
setter = cls.LookupDynamicFunctionAllowPrivate(setter_name);
if (!setter.IsNull()) {
break;
@@ -4070,16 +4074,20 @@
cls = cls.SuperClass();
}
- if (setter.IsNull()) {
- return Api::NewError("%s: did not find instance field '%s'.",
- CURRENT_FUNC, field_name.ToCString());
- }
-
// Invoke the setter and return the result.
const int kNumArgs = 2;
const Array& args = Array::Handle(isolate, Array::New(kNumArgs));
args.SetAt(0, instance);
args.SetAt(1, value_instance);
+ if (setter.IsNull()) {
+ const Array& args_descriptor =
+ Array::Handle(ArgumentsDescriptor::New(args.Length()));
+ return Api::NewHandle(isolate,
+ DartEntry::InvokeNoSuchMethod(instance,
+ setter_name,
+ args,
+ args_descriptor));
+ }
return Api::NewHandle(isolate, DartEntry::InvokeFunction(setter, args));
} else if (obj.IsClass()) {
diff --git a/runtime/vm/dart_api_impl_test.cc b/runtime/vm/dart_api_impl_test.cc
index 6008c9c..b3e96e6 100644
--- a/runtime/vm/dart_api_impl_test.cc
+++ b/runtime/vm/dart_api_impl_test.cc
@@ -3755,7 +3755,8 @@
// Instance method, wrong arg count.
EXPECT_ERROR(Dart_Invoke(instance, name, 2, bad_args),
- "did not find instance method 'Methods.instanceMethod'");
+ "Class 'Methods' has no instance method 'instanceMethod'"
+ " with matching arguments");
name = PrivateLibName(lib, "_instanceMethod");
EXPECT(Dart_IsError(Dart_Invoke(lib, name, 1, args)));
@@ -3888,15 +3889,77 @@
EXPECT_VALID(Dart_StringToCString(result, &value));
EXPECT_STREQ("null", value);
- // Should throw a NullPointerException. Disabled due to bug 5415268.
- /*
- Dart_Handle function_name2 = NewString("NoNoNo");
- result = Dart_Invoke(null_receiver,
- function_name2,
- number_of_arguments,
- dart_arguments);
- EXPECT(Dart_IsError(result));
- EXPECT(Dart_ErrorHasException(result)); */
+ Dart_Handle function_name = NewString("NoNoNo");
+ result = Dart_Invoke(Dart_Null(),
+ function_name,
+ 0,
+ NULL);
+ EXPECT(Dart_IsError(result));
+ EXPECT(Dart_ErrorHasException(result));
+}
+
+
+TEST_CASE(InvokeNoSuchMethod) {
+ const char* kScriptChars =
+ "import 'dart:_collection-dev' as _collection_dev;\n"
+ "class Expect {\n"
+ " static equals(a, b) {\n"
+ " if (a != b) {\n"
+ " throw 'not equal. expected: $a, got: $b';\n"
+ " }\n"
+ " }\n"
+ "}\n"
+ "class TestClass {\n"
+ " static int fld1 = 0;\n"
+ " void noSuchMethod(Invocation invocation) {\n"
+ " var name = _collection_dev.Symbol.getName(invocation.memberName);\n"
+ " if (name == 'fld') {\n"
+ " Expect.equals(true, invocation.isGetter);\n"
+ " Expect.equals(false, invocation.isMethod);\n"
+ " Expect.equals(false, invocation.isSetter);\n"
+ " } else if (name == 'setfld') {\n"
+ " Expect.equals(true, invocation.isSetter);\n"
+ " Expect.equals(false, invocation.isMethod);\n"
+ " Expect.equals(false, invocation.isGetter);\n"
+ " } else if (name == 'method') {\n"
+ " Expect.equals(true, invocation.isMethod);\n"
+ " Expect.equals(false, invocation.isSetter);\n"
+ " Expect.equals(false, invocation.isGetter);\n"
+ " }\n"
+ " TestClass.fld1 += 1;\n"
+ " }\n"
+ " static TestClass testMain() {\n"
+ " return new TestClass();\n"
+ " }\n"
+ "}\n";
+ Dart_Handle result;
+ Dart_Handle instance;
+ // Create a test library and Load up a test script in it.
+ Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL);
+ Dart_Handle cls = Dart_GetClass(lib, NewString("TestClass"));
+ EXPECT_VALID(cls);
+
+ // Invoke a function which returns an object.
+ instance = Dart_Invoke(cls, NewString("testMain"), 0, NULL);
+ EXPECT_VALID(instance);
+
+ // Try to get a field that does not exist, should call noSuchMethod.
+ result = Dart_GetField(instance, NewString("fld"));
+ EXPECT_VALID(result);
+
+ // Try to set a field that does not exist, should call noSuchMethod.
+ result = Dart_SetField(instance, NewString("setfld"), Dart_NewInteger(13));
+ EXPECT_VALID(result);
+
+ // Try to invoke a method that does not exist, should call noSuchMethod.
+ result = Dart_Invoke(instance, NewString("method"), 0, NULL);
+ EXPECT_VALID(result);
+
+ result = Dart_GetField(cls, NewString("fld1"));
+ EXPECT_VALID(result);
+ int64_t value = 0;
+ result = Dart_IntegerToInt64(result, &value);
+ EXPECT_EQ(3, value);
}
diff --git a/runtime/vm/deopt_instructions.cc b/runtime/vm/deopt_instructions.cc
index fce3e71..9054358 100644
--- a/runtime/vm/deopt_instructions.cc
+++ b/runtime/vm/deopt_instructions.cc
@@ -41,12 +41,12 @@
intptr_t DeoptimizationContext::GetFromFp() const {
- return from_frame_[from_frame_size_ - num_args_ - kLastParamSlotIndex];
+ return from_frame_[from_frame_size_ - num_args_ - 1 - kParamEndSlotFromFp];
}
intptr_t DeoptimizationContext::GetFromPc() const {
- return from_frame_[from_frame_size_ - num_args_ + kPcSlotIndexFromSp];
+ return from_frame_[from_frame_size_ - num_args_ + kSavedPcSlotFromSp];
}
intptr_t DeoptimizationContext::GetCallerFp() const {
@@ -72,18 +72,14 @@
virtual DeoptInstr::Kind kind() const { return kStackSlot; }
virtual const char* ToCString() const {
- const char* format = "s%"Pd"";
- intptr_t len = OS::SNPrint(NULL, 0, format, stack_slot_index_);
- char* chars = Isolate::Current()->current_zone()->Alloc<char>(len + 1);
- OS::SNPrint(chars, len + 1, format, stack_slot_index_);
- return chars;
+ return Isolate::Current()->current_zone()->PrintToString(
+ "s%"Pd"", stack_slot_index_);
}
- void Execute(DeoptimizationContext* deopt_context, intptr_t to_index) {
+ void Execute(DeoptimizationContext* deopt_context, intptr_t* to_addr) {
intptr_t from_index =
deopt_context->from_frame_size() - stack_slot_index_ - 1;
intptr_t* from_addr = deopt_context->GetFromFrameAddressAt(from_index);
- intptr_t* to_addr = deopt_context->GetToFrameAddressAt(to_index);
*to_addr = *from_addr;
}
@@ -105,19 +101,15 @@
virtual DeoptInstr::Kind kind() const { return kDoubleStackSlot; }
virtual const char* ToCString() const {
- const char* format = "ds%"Pd"";
- intptr_t len = OS::SNPrint(NULL, 0, format, stack_slot_index_);
- char* chars = Isolate::Current()->current_zone()->Alloc<char>(len + 1);
- OS::SNPrint(chars, len + 1, format, stack_slot_index_);
- return chars;
+ return Isolate::Current()->current_zone()->PrintToString(
+ "ds%"Pd"", stack_slot_index_);
}
- void Execute(DeoptimizationContext* deopt_context, intptr_t to_index) {
+ void Execute(DeoptimizationContext* deopt_context, intptr_t* to_addr) {
intptr_t from_index =
deopt_context->from_frame_size() - stack_slot_index_ - 1;
double* from_addr = reinterpret_cast<double*>(
deopt_context->GetFromFrameAddressAt(from_index));
- intptr_t* to_addr = deopt_context->GetToFrameAddressAt(to_index);
*reinterpret_cast<RawSmi**>(to_addr) = Smi::New(0);
Isolate::Current()->DeferDoubleMaterialization(
*from_addr, reinterpret_cast<RawDouble**>(to_addr));
@@ -141,19 +133,15 @@
virtual DeoptInstr::Kind kind() const { return kInt64StackSlot; }
virtual const char* ToCString() const {
- const char* format = "ms%"Pd"";
- intptr_t len = OS::SNPrint(NULL, 0, format, stack_slot_index_);
- char* chars = Isolate::Current()->current_zone()->Alloc<char>(len + 1);
- OS::SNPrint(chars, len + 1, format, stack_slot_index_);
- return chars;
+ return Isolate::Current()->current_zone()->PrintToString(
+ "ms%"Pd"", stack_slot_index_);
}
- void Execute(DeoptimizationContext* deopt_context, intptr_t to_index) {
+ void Execute(DeoptimizationContext* deopt_context, intptr_t* to_addr) {
intptr_t from_index =
deopt_context->from_frame_size() - stack_slot_index_ - 1;
int64_t* from_addr = reinterpret_cast<int64_t*>(
deopt_context->GetFromFrameAddressAt(from_index));
- intptr_t* to_addr = deopt_context->GetToFrameAddressAt(to_index);
*reinterpret_cast<RawSmi**>(to_addr) = Smi::New(0);
if (Smi::IsValid64(*from_addr)) {
*to_addr = reinterpret_cast<intptr_t>(
@@ -182,19 +170,15 @@
virtual DeoptInstr::Kind kind() const { return kFloat32x4StackSlot; }
virtual const char* ToCString() const {
- const char* format = "f32x4s%"Pd"";
- intptr_t len = OS::SNPrint(NULL, 0, format, stack_slot_index_);
- char* chars = Isolate::Current()->current_zone()->Alloc<char>(len + 1);
- OS::SNPrint(chars, len + 1, format, stack_slot_index_);
- return chars;
+ return Isolate::Current()->current_zone()->PrintToString(
+ "f32x4s%"Pd"", stack_slot_index_);
}
- void Execute(DeoptimizationContext* deopt_context, intptr_t to_index) {
+ void Execute(DeoptimizationContext* deopt_context, intptr_t* to_addr) {
intptr_t from_index =
deopt_context->from_frame_size() - stack_slot_index_ - 1;
simd128_value_t* from_addr = reinterpret_cast<simd128_value_t*>(
deopt_context->GetFromFrameAddressAt(from_index));
- intptr_t* to_addr = deopt_context->GetToFrameAddressAt(to_index);
*reinterpret_cast<RawSmi**>(to_addr) = Smi::New(0);
Isolate::Current()->DeferFloat32x4Materialization(
*from_addr, reinterpret_cast<RawFloat32x4**>(to_addr));
@@ -218,19 +202,15 @@
virtual DeoptInstr::Kind kind() const { return kUint32x4StackSlot; }
virtual const char* ToCString() const {
- const char* format = "ui32x4s%"Pd"";
- intptr_t len = OS::SNPrint(NULL, 0, format, stack_slot_index_);
- char* chars = Isolate::Current()->current_zone()->Alloc<char>(len + 1);
- OS::SNPrint(chars, len + 1, format, stack_slot_index_);
- return chars;
+ return Isolate::Current()->current_zone()->PrintToString(
+ "ui32x4s%"Pd"", stack_slot_index_);
}
- void Execute(DeoptimizationContext* deopt_context, intptr_t to_index) {
+ void Execute(DeoptimizationContext* deopt_context, intptr_t* to_addr) {
intptr_t from_index =
deopt_context->from_frame_size() - stack_slot_index_ - 1;
simd128_value_t* from_addr = reinterpret_cast<simd128_value_t*>(
deopt_context->GetFromFrameAddressAt(from_index));
- intptr_t* to_addr = deopt_context->GetToFrameAddressAt(to_index);
*reinterpret_cast<RawSmi**>(to_addr) = Smi::New(0);
Isolate::Current()->DeferUint32x4Materialization(
*from_addr, reinterpret_cast<RawUint32x4**>(to_addr));
@@ -270,7 +250,7 @@
"ret oti:%"Pd"(%"Pd")", object_table_index_, deopt_id_);
}
- void Execute(DeoptimizationContext* deopt_context, intptr_t to_index) {
+ void Execute(DeoptimizationContext* deopt_context, intptr_t* to_addr) {
Function& function = Function::Handle(deopt_context->isolate());
function ^= deopt_context->ObjectAt(object_table_index_);
const Code& code =
@@ -279,7 +259,6 @@
uword continue_at_pc = code.GetPcForDeoptId(deopt_id_,
PcDescriptors::kDeopt);
ASSERT(continue_at_pc != 0);
- intptr_t* to_addr = deopt_context->GetToFrameAddressAt(to_index);
*to_addr = continue_at_pc;
uword pc = code.GetPcForDeoptId(deopt_id_, PcDescriptors::kIcCall);
@@ -321,19 +300,14 @@
virtual DeoptInstr::Kind kind() const { return kConstant; }
virtual const char* ToCString() const {
- const char* format = "const oti:%"Pd"";
- intptr_t len = OS::SNPrint(NULL, 0, format, object_table_index_);
- char* chars = Isolate::Current()->current_zone()->Alloc<char>(len + 1);
- OS::SNPrint(chars, len + 1, format, object_table_index_);
- return chars;
+ return Isolate::Current()->current_zone()->PrintToString(
+ "const oti:%"Pd"", object_table_index_);
}
- void Execute(DeoptimizationContext* deopt_context, intptr_t to_index) {
+ void Execute(DeoptimizationContext* deopt_context, intptr_t* to_addr) {
const Object& obj = Object::Handle(
deopt_context->isolate(), deopt_context->ObjectAt(object_table_index_));
- RawObject** to_addr = reinterpret_cast<RawObject**>(
- deopt_context->GetToFrameAddressAt(to_index));
- *to_addr = obj.raw();
+ *reinterpret_cast<RawObject**>(to_addr) = obj.raw();
}
private:
@@ -356,10 +330,8 @@
return Assembler::RegisterName(reg_);
}
- void Execute(DeoptimizationContext* deopt_context, intptr_t to_index) {
- intptr_t value = deopt_context->RegisterValue(reg_);
- intptr_t* to_addr = deopt_context->GetToFrameAddressAt(to_index);
- *to_addr = value;
+ void Execute(DeoptimizationContext* deopt_context, intptr_t* to_addr) {
+ *to_addr = deopt_context->RegisterValue(reg_);
}
private:
@@ -382,9 +354,8 @@
return Assembler::FpuRegisterName(reg_);
}
- void Execute(DeoptimizationContext* deopt_context, intptr_t to_index) {
+ void Execute(DeoptimizationContext* deopt_context, intptr_t* to_addr) {
double value = deopt_context->FpuRegisterValue(reg_);
- intptr_t* to_addr = deopt_context->GetToFrameAddressAt(to_index);
*reinterpret_cast<RawSmi**>(to_addr) = Smi::New(0);
Isolate::Current()->DeferDoubleMaterialization(
value, reinterpret_cast<RawDouble**>(to_addr));
@@ -406,17 +377,12 @@
virtual DeoptInstr::Kind kind() const { return kInt64FpuRegister; }
virtual const char* ToCString() const {
- const char* format = "%s(m)";
- intptr_t len =
- OS::SNPrint(NULL, 0, format, Assembler::FpuRegisterName(reg_));
- char* chars = Isolate::Current()->current_zone()->Alloc<char>(len + 1);
- OS::SNPrint(chars, len + 1, format, Assembler::FpuRegisterName(reg_));
- return chars;
+ return Isolate::Current()->current_zone()->PrintToString(
+ "%s(m)", Assembler::FpuRegisterName(reg_));
}
- void Execute(DeoptimizationContext* deopt_context, intptr_t to_index) {
+ void Execute(DeoptimizationContext* deopt_context, intptr_t* to_addr) {
int64_t value = deopt_context->FpuRegisterValueAsInt64(reg_);
- intptr_t* to_addr = deopt_context->GetToFrameAddressAt(to_index);
*reinterpret_cast<RawSmi**>(to_addr) = Smi::New(0);
if (Smi::IsValid64(value)) {
*to_addr = reinterpret_cast<intptr_t>(
@@ -444,17 +410,12 @@
virtual DeoptInstr::Kind kind() const { return kFloat32x4FpuRegister; }
virtual const char* ToCString() const {
- const char* format = "%s(f32x4)";
- intptr_t len =
- OS::SNPrint(NULL, 0, format, Assembler::FpuRegisterName(reg_));
- char* chars = Isolate::Current()->current_zone()->Alloc<char>(len + 1);
- OS::SNPrint(chars, len + 1, format, Assembler::FpuRegisterName(reg_));
- return chars;
+ return Isolate::Current()->current_zone()->PrintToString(
+ "%s(f32x4)", Assembler::FpuRegisterName(reg_));
}
- void Execute(DeoptimizationContext* deopt_context, intptr_t to_index) {
+ void Execute(DeoptimizationContext* deopt_context, intptr_t* to_addr) {
simd128_value_t value = deopt_context->FpuRegisterValueAsSimd128(reg_);
- intptr_t* to_addr = deopt_context->GetToFrameAddressAt(to_index);
*reinterpret_cast<RawSmi**>(to_addr) = Smi::New(0);
Isolate::Current()->DeferFloat32x4Materialization(
value, reinterpret_cast<RawFloat32x4**>(to_addr));
@@ -477,17 +438,12 @@
virtual DeoptInstr::Kind kind() const { return kFloat32x4FpuRegister; }
virtual const char* ToCString() const {
- const char* format = "%s(f32x4)";
- intptr_t len =
- OS::SNPrint(NULL, 0, format, Assembler::FpuRegisterName(reg_));
- char* chars = Isolate::Current()->current_zone()->Alloc<char>(len + 1);
- OS::SNPrint(chars, len + 1, format, Assembler::FpuRegisterName(reg_));
- return chars;
+ return Isolate::Current()->current_zone()->PrintToString(
+ "%s(f32x4)", Assembler::FpuRegisterName(reg_));
}
- void Execute(DeoptimizationContext* deopt_context, intptr_t to_index) {
+ void Execute(DeoptimizationContext* deopt_context, intptr_t* to_addr) {
simd128_value_t value = deopt_context->FpuRegisterValueAsSimd128(reg_);
- intptr_t* to_addr = deopt_context->GetToFrameAddressAt(to_index);
*reinterpret_cast<RawSmi**>(to_addr) = Smi::New(0);
Isolate::Current()->DeferUint32x4Materialization(
value, reinterpret_cast<RawUint32x4**>(to_addr));
@@ -513,22 +469,18 @@
virtual DeoptInstr::Kind kind() const { return kPcMarker; }
virtual const char* ToCString() const {
- const char* format = "pcmark oti:%"Pd"";
- intptr_t len = OS::SNPrint(NULL, 0, format, object_table_index_);
- char* chars = Isolate::Current()->current_zone()->Alloc<char>(len + 1);
- OS::SNPrint(chars, len + 1, format, object_table_index_);
- return chars;
+ return Isolate::Current()->current_zone()->PrintToString(
+ "pcmark oti:%"Pd"", object_table_index_);
}
- void Execute(DeoptimizationContext* deopt_context, intptr_t to_index) {
+ void Execute(DeoptimizationContext* deopt_context, intptr_t* to_addr) {
Function& function = Function::Handle(deopt_context->isolate());
function ^= deopt_context->ObjectAt(object_table_index_);
const Code& code =
Code::Handle(deopt_context->isolate(), function.unoptimized_code());
ASSERT(!code.IsNull());
- intptr_t pc_marker = code.EntryPoint() +
- Assembler::kOffsetOfSavedPCfromEntrypoint;
- intptr_t* to_addr = deopt_context->GetToFrameAddressAt(to_index);
+ const intptr_t pc_marker =
+ code.EntryPoint() + Assembler::kEntryPointToPcMarkerOffset;
*to_addr = pc_marker;
// Increment the deoptimization counter. This effectively increments each
// function occurring in the optimized frame.
@@ -563,10 +515,8 @@
return "callerfp";
}
- void Execute(DeoptimizationContext* deopt_context, intptr_t to_index) {
- intptr_t from = deopt_context->GetCallerFp();
- intptr_t* to_addr = deopt_context->GetToFrameAddressAt(to_index);
- *to_addr = from;
+ void Execute(DeoptimizationContext* deopt_context, intptr_t* to_addr) {
+ *to_addr = deopt_context->GetCallerFp();
deopt_context->SetCallerFp(reinterpret_cast<intptr_t>(to_addr));
}
@@ -588,10 +538,8 @@
return "callerpc";
}
- void Execute(DeoptimizationContext* deopt_context, intptr_t to_index) {
- intptr_t from = deopt_context->GetFromPc();
- intptr_t* to_addr = deopt_context->GetToFrameAddressAt(to_index);
- *to_addr = from;
+ void Execute(DeoptimizationContext* deopt_context, intptr_t* to_addr) {
+ *to_addr = deopt_context->GetFromPc();
}
private:
@@ -623,14 +571,11 @@
virtual DeoptInstr::Kind kind() const { return kSuffix; }
virtual const char* ToCString() const {
- const char* format = "suffix %"Pd":%"Pd;
- intptr_t len = OS::SNPrint(NULL, 0, format, info_number_, suffix_length_);
- char* chars = Isolate::Current()->current_zone()->Alloc<char>(len + 1);
- OS::SNPrint(chars, len + 1, format, info_number_, suffix_length_);
- return chars;
+ return Isolate::Current()->current_zone()->PrintToString(
+ "suffix %"Pd":%"Pd, info_number_, suffix_length_);
}
- void Execute(DeoptimizationContext* deopt_context, intptr_t to_index) {
+ void Execute(DeoptimizationContext* deopt_context, intptr_t* to_addr) {
// The deoptimization info is uncompressed by translating away suffixes
// before executing the instructions.
UNREACHABLE();
@@ -652,6 +597,66 @@
};
+// Write reference to a materialized object with the given index into the
+// stack slot.
+class DeoptMaterializedObjectRefInstr : public DeoptInstr {
+ public:
+ explicit DeoptMaterializedObjectRefInstr(intptr_t index)
+ : index_(index) {
+ ASSERT(index >= 0);
+ }
+
+ virtual intptr_t from_index() const { return index_; }
+ virtual DeoptInstr::Kind kind() const { return kMaterializedObjectRef; }
+
+ virtual const char* ToCString() const {
+ return Isolate::Current()->current_zone()->PrintToString(
+ "mat ref #%"Pd"", index_);
+ }
+
+ void Execute(DeoptimizationContext* deopt_context, intptr_t* to_addr) {
+ *reinterpret_cast<RawSmi**>(to_addr) = Smi::New(0);
+ Isolate::Current()->DeferMaterializedObjectRef(
+ index_, to_addr);
+ }
+
+ private:
+ intptr_t index_;
+
+ DISALLOW_COPY_AND_ASSIGN(DeoptMaterializedObjectRefInstr);
+};
+
+
+// Materialize object with the given number of fields.
+// Arguments for materialization (class and field-value pairs) are pushed
+// to the expression stack of the bottom-most frame.
+class DeoptMaterializeObjectInstr : public DeoptInstr {
+ public:
+ explicit DeoptMaterializeObjectInstr(intptr_t field_count)
+ : field_count_(field_count) {
+ ASSERT(field_count >= 0);
+ }
+
+ virtual intptr_t from_index() const { return field_count_; }
+ virtual DeoptInstr::Kind kind() const { return kMaterializeObject; }
+
+ virtual const char* ToCString() const {
+ return Isolate::Current()->current_zone()->PrintToString(
+ "mat obj len:%"Pd"", field_count_);
+ }
+
+ void Execute(DeoptimizationContext* deopt_context, intptr_t* to_addr) {
+ // This instructions are executed manually by the DeoptimizeWithDeoptInfo.
+ UNREACHABLE();
+ }
+
+ private:
+ intptr_t field_count_;
+
+ DISALLOW_COPY_AND_ASSIGN(DeoptMaterializeObjectInstr);
+};
+
+
intptr_t DeoptInstr::DecodeSuffix(intptr_t from_index, intptr_t* info_number) {
*info_number = DeoptSuffixInstr::InfoNumber::decode(from_index);
return DeoptSuffixInstr::SuffixLength::decode(from_index);
@@ -700,6 +705,9 @@
case kCallerFp: return new DeoptCallerFpInstr();
case kCallerPc: return new DeoptCallerPcInstr();
case kSuffix: return new DeoptSuffixInstr(from_index);
+ case kMaterializedObjectRef:
+ return new DeoptMaterializedObjectRefInstr(from_index);
+ case kMaterializeObject: return new DeoptMaterializeObjectInstr(from_index);
}
UNREACHABLE();
return NULL;
@@ -743,7 +751,9 @@
object_table_(GrowableObjectArray::Handle(GrowableObjectArray::New())),
num_args_(num_args),
trie_root_(new TrieNode()),
- current_info_number_(0) {
+ current_info_number_(0),
+ frame_start_(-1),
+ materializations_() {
}
@@ -763,7 +773,7 @@
intptr_t DeoptInfoBuilder::CalculateStackIndex(const Location& from_loc) const {
return from_loc.stack_index() < 0 ?
from_loc.stack_index() + num_args_ :
- from_loc.stack_index() + num_args_ - kFirstLocalSlotIndex + 1;
+ from_loc.stack_index() + num_args_ - kFirstLocalSlotFromFp + 1;
}
@@ -778,7 +788,7 @@
(code.GetPcForDeoptId(deopt_id, PcDescriptors::kDeopt) != 0));
#endif
const intptr_t object_table_index = FindOrAddObjectInTable(function);
- ASSERT(to_index == instructions_.length());
+ ASSERT(to_index == FrameSize());
instructions_.Add(new DeoptRetAddressInstr(object_table_index, deopt_id));
}
@@ -787,7 +797,7 @@
intptr_t to_index) {
// Function object was already added by AddReturnAddress, find it.
intptr_t from_index = FindOrAddObjectInTable(function);
- ASSERT(to_index == instructions_.length());
+ ASSERT(to_index == FrameSize());
instructions_.Add(new DeoptPcMarkerInstr(from_index));
}
@@ -833,35 +843,102 @@
ASSERT(value->definition()->representation() == kUnboxedUint32x4);
deopt_instr = new DeoptUint32x4StackSlotInstr(from_index);
}
+ } else if (from_loc.IsInvalid() &&
+ value->definition()->IsMaterializeObject()) {
+ const intptr_t index = FindMaterialization(
+ value->definition()->AsMaterializeObject());
+ ASSERT(index >= 0);
+ deopt_instr = new DeoptMaterializedObjectRefInstr(index);
} else {
UNREACHABLE();
}
- ASSERT(to_index == instructions_.length());
+ ASSERT(to_index == FrameSize());
ASSERT(deopt_instr != NULL);
instructions_.Add(deopt_instr);
}
void DeoptInfoBuilder::AddCallerFp(intptr_t to_index) {
- ASSERT(to_index == instructions_.length());
+ ASSERT(to_index == FrameSize());
instructions_.Add(new DeoptCallerFpInstr());
}
void DeoptInfoBuilder::AddCallerPc(intptr_t to_index) {
- ASSERT(to_index == instructions_.length());
+ ASSERT(to_index == FrameSize());
instructions_.Add(new DeoptCallerPcInstr());
}
+void DeoptInfoBuilder::AddConstant(const Object& obj, intptr_t to_index) {
+ ASSERT(to_index == FrameSize());
+ intptr_t object_table_index = FindOrAddObjectInTable(obj);
+ instructions_.Add(new DeoptConstantInstr(object_table_index));
+}
+
+
+void DeoptInfoBuilder::AddMaterialization(MaterializeObjectInstr* mat) {
+ const intptr_t index = FindMaterialization(mat);
+ if (index >= 0) {
+ return; // Already added.
+ }
+ materializations_.Add(mat);
+
+ // Count initialized fields and emit kMaterializeObject instruction.
+ // There is no need to write nulls into fields because object is null
+ // initialized by default.
+ intptr_t non_null_fields = 0;
+ for (intptr_t i = 0; i < mat->InputCount(); i++) {
+ if (!mat->InputAt(i)->BindsToConstantNull()) {
+ non_null_fields++;
+ }
+ }
+
+ instructions_.Add(new DeoptMaterializeObjectInstr(non_null_fields));
+}
+
+
+intptr_t DeoptInfoBuilder::EmitMaterializationArguments() {
+ intptr_t slot_idx = 1; // Return address is emitted at 0.
+ for (intptr_t i = 0; i < materializations_.length(); i++) {
+ MaterializeObjectInstr* mat = materializations_[i];
+ AddConstant(mat->cls(), slot_idx++); // Class of the instance to allocate.
+ for (intptr_t i = 0; i < mat->InputCount(); i++) {
+ if (!mat->InputAt(i)->BindsToConstantNull()) {
+ // Emit field-value pair.
+ AddConstant(mat->FieldAt(i), slot_idx++);
+ AddCopy(mat->InputAt(i), mat->LocationAt(i), slot_idx++);
+ }
+ }
+ }
+ return slot_idx;
+}
+
+
+intptr_t DeoptInfoBuilder::FindMaterialization(
+ MaterializeObjectInstr* mat) const {
+ for (intptr_t i = 0; i < materializations_.length(); i++) {
+ if (materializations_[i] == mat) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+
RawDeoptInfo* DeoptInfoBuilder::CreateDeoptInfo() {
+ // TODO(vegorov): enable compression of deoptimization info containing object
+ // materialization instructions.
+ const bool disable_compression =
+ (instructions_[0]->kind() == DeoptInstr::kMaterializeObject);
+
intptr_t length = instructions_.length();
// Count the number of instructions that are a shared suffix of some deopt
// info already written.
TrieNode* suffix = trie_root_;
intptr_t suffix_length = 0;
- if (FLAG_compress_deopt_info) {
+ if (FLAG_compress_deopt_info && !disable_compression) {
for (intptr_t i = length - 1; i >= 0; --i) {
TrieNode* node = suffix->FindChild(*instructions_[i]);
if (node == NULL) break;
@@ -894,6 +971,9 @@
}
instructions_.Clear();
+ materializations_.Clear();
+ frame_start_ = -1;
+
++current_info_number_;
return deopt_info.raw();
}
diff --git a/runtime/vm/deopt_instructions.h b/runtime/vm/deopt_instructions.h
index 0d41ffd..024eb93 100644
--- a/runtime/vm/deopt_instructions.h
+++ b/runtime/vm/deopt_instructions.h
@@ -15,6 +15,7 @@
class Location;
class Value;
+class MaterializeObjectInstr;
// Holds all data relevant for execution of deoptimization instructions.
class DeoptimizationContext : public ValueObject {
@@ -111,6 +112,8 @@
kCallerFp,
kCallerPc,
kSuffix,
+ kMaterializedObjectRef,
+ kMaterializeObject
};
static DeoptInstr* Create(intptr_t kind_as_int, intptr_t from_index);
@@ -121,7 +124,7 @@
virtual const char* ToCString() const = 0;
virtual void Execute(DeoptimizationContext* deopt_context,
- intptr_t to_index) = 0;
+ intptr_t* to_addr) = 0;
virtual DeoptInstr::Kind kind() const = 0;
@@ -139,11 +142,18 @@
const Array& object_table,
Function* func);
- protected:
- virtual intptr_t from_index() const = 0;
+ // Return number of initialized fields in the object that will be
+ // materialized by kMaterializeObject instruction.
+ static intptr_t GetFieldCount(DeoptInstr* instr) {
+ ASSERT(instr->kind() == DeoptInstr::kMaterializeObject);
+ return instr->from_index();
+ }
+ protected:
friend class DeoptInfoBuilder;
+ virtual intptr_t from_index() const = 0;
+
private:
DISALLOW_COPY_AND_ASSIGN(DeoptInstr);
};
@@ -173,14 +183,42 @@
void AddCallerFp(intptr_t to_index);
void AddCallerPc(intptr_t to_index);
+ // Add object to be materialized. Emit kMaterializeObject instruction.
+ void AddMaterialization(MaterializeObjectInstr* mat);
+
+ // For every materialized object emit instructions describing data required
+ // for materialization: class of the instance to allocate and field-value
+ // pairs for initialization.
+ // Emitted instructions are expected to follow return-address slot emitted
+ // first. This way they become a part of the bottom-most deoptimized frame
+ // and are discoverable by GC.
+ // At deoptimization they will be removed by the stub at the very end:
+ // after they were used to materialize objects.
+ // Returns the index of the next stack slot. Used for verification.
+ intptr_t EmitMaterializationArguments();
+
RawDeoptInfo* CreateDeoptInfo();
+ // Mark the actual start of the frame description after all materialization
+ // instructions were emitted. Used for verification purposes.
+ void MarkFrameStart() {
+ ASSERT(frame_start_ == -1);
+ frame_start_ = instructions_.length();
+ }
+
private:
class TrieNode;
intptr_t FindOrAddObjectInTable(const Object& obj) const;
+ intptr_t FindMaterialization(MaterializeObjectInstr* mat) const;
intptr_t CalculateStackIndex(const Location& from_loc) const;
+ intptr_t FrameSize() const {
+ return instructions_.length() - frame_start_;
+ }
+
+ void AddConstant(const Object& obj, intptr_t to_index);
+
GrowableArray<DeoptInstr*> instructions_;
const GrowableObjectArray& object_table_;
const intptr_t num_args_;
@@ -189,6 +227,9 @@
TrieNode* trie_root_;
intptr_t current_info_number_;
+ intptr_t frame_start_;
+ GrowableArray<MaterializeObjectInstr*> materializations_;
+
DISALLOW_COPY_AND_ASSIGN(DeoptInfoBuilder);
};
diff --git a/runtime/vm/disassembler_arm.cc b/runtime/vm/disassembler_arm.cc
index a9cc7f0..8a7b5ea 100644
--- a/runtime/vm/disassembler_arm.cc
+++ b/runtime/vm/disassembler_arm.cc
@@ -601,8 +601,8 @@
break;
}
case 7: {
- if (instr->Bits(21, 2) == 0x1) {
- Format(instr, "bkpt'cond #'imm12_4");
+ if ((instr->Bits(21, 2) == 0x1) && (instr->ConditionField() == AL)) {
+ Format(instr, "bkpt #'imm12_4");
} else {
// Format(instr, "smc'cond");
Unknown(instr); // Not used.
@@ -861,9 +861,9 @@
void ARMDecoder::DecodeType3(Instr* instr) {
if (instr->IsDivision()) {
if (instr->Bit(21)) {
- Format(instr, "udiv'cond 'rd, 'rn, 'rm");
+ Format(instr, "udiv'cond 'rn, 'rs, 'rm");
} else {
- Format(instr, "sdiv'cond 'rd, 'rn, 'rm");
+ Format(instr, "sdiv'cond 'rn, 'rs, 'rm");
}
return;
}
diff --git a/runtime/vm/flow_graph.cc b/runtime/vm/flow_graph.cc
index 34ca693..ae130d4 100644
--- a/runtime/vm/flow_graph.cc
+++ b/runtime/vm/flow_graph.cc
@@ -29,17 +29,17 @@
preorder_(),
postorder_(),
reverse_postorder_(),
- block_effects_(NULL) {
+ block_effects_(NULL),
+ licm_allowed_(true) {
DiscoverBlocks();
}
-ConstantInstr* FlowGraph::AddConstantToInitialDefinitions(
- const Object& object) {
+ConstantInstr* FlowGraph::GetConstant(const Object& object) {
// Check if the constant is already in the pool.
- for (intptr_t i = 0; i < graph_entry_->initial_definitions()->length(); ++i) {
- ConstantInstr* constant =
- (*graph_entry_->initial_definitions())[i]->AsConstant();
+ GrowableArray<Definition*>* pool = graph_entry_->initial_definitions();
+ for (intptr_t i = 0; i < pool->length(); ++i) {
+ ConstantInstr* constant = (*pool)[i]->AsConstant();
if ((constant != NULL) && (constant->value().raw() == object.raw())) {
return constant;
}
@@ -51,6 +51,7 @@
return constant;
}
+
void FlowGraph::AddToInitialDefinitions(Definition* defn) {
// TODO(zerny): Set previous to the graph entry so it is accessible by
// GetBlock. Remove this once there is a direct pointer to the block.
@@ -522,7 +523,6 @@
dom_index = idom[dom_index];
}
idom[block_index] = dom_index;
- preorder_[block_index]->set_dominator(preorder_[dom_index]);
preorder_[dom_index]->AddDominatedBlock(preorder_[block_index]);
}
@@ -626,8 +626,7 @@
GrowableArray<Definition*> env(variable_count());
// Add global constants to the initial definitions.
- constant_null_ =
- AddConstantToInitialDefinitions(Object::ZoneHandle());
+ constant_null_ = GetConstant(Object::ZoneHandle());
// Add parameters to the initial definitions and renaming environment.
if (inlining_parameters != NULL) {
@@ -699,7 +698,6 @@
AttachEnvironment(block_entry, env);
// 2. Process normal instructions.
-
for (ForwardInstructionIterator it(block_entry); !it.Done(); it.Advance()) {
Instruction* current = it.Current();
@@ -709,18 +707,20 @@
}
// 2a. Handle uses:
- // Update expression stack environment for each use.
- // For each use of a LoadLocal or StoreLocal: Replace it with the value
- // from the environment.
+ // Update the expression stack renaming environment for each use by
+ // removing the renamed value.
+ // For each use of a LoadLocal, StoreLocal, or Constant: Replace it with
+ // the renamed value.
for (intptr_t i = current->InputCount() - 1; i >= 0; --i) {
Value* v = current->InputAt(i);
// Update expression stack.
ASSERT(env->length() > variable_count());
Definition* reaching_defn = env->RemoveLast();
-
Definition* input_defn = v->definition();
- if (input_defn->IsLoadLocal() || input_defn->IsStoreLocal()) {
+ if (input_defn->IsLoadLocal() ||
+ input_defn->IsStoreLocal() ||
+ input_defn->IsConstant()) {
// Remove the load/store from the graph.
input_defn->RemoveFromGraph();
// Assert we are not referencing nulls in the initial environment.
@@ -736,14 +736,13 @@
env->RemoveLast();
}
- // 2b. Handle LoadLocal and StoreLocal.
- // For each LoadLocal: Remove it from the graph.
- // For each StoreLocal: Remove it from the graph and update the environment.
+ // 2b. Handle LoadLocal, StoreLocal, and Constant.
Definition* definition = current->AsDefinition();
if (definition != NULL) {
LoadLocalInstr* load = definition->AsLoadLocal();
StoreLocalInstr* store = definition->AsStoreLocal();
- if ((load != NULL) || (store != NULL)) {
+ ConstantInstr* constant = definition->AsConstant();
+ if ((load != NULL) || (store != NULL) || (constant != NULL)) {
intptr_t index;
Definition* result;
if (store != NULL) {
@@ -756,7 +755,7 @@
} else {
(*env)[index] = constant_null();
}
- } else {
+ } else if (load != NULL) {
// The graph construction ensures we do not have an unused LoadLocal
// computation.
ASSERT(definition->is_used());
@@ -772,16 +771,21 @@
if (variable_liveness->IsLastLoad(block_entry, load)) {
(*env)[index] = constant_null();
}
+ } else {
+ ASSERT(definition->is_used());
+ result = GetConstant(constant->value());
}
// Update expression stack or remove from graph.
if (definition->is_used()) {
+ ASSERT(result != NULL);
env->Add(result);
- // We remove load/store instructions when we find their use in 2a.
+ // We remove load/store/constant instructions when we find their
+ // use in 2a.
} else {
it.RemoveCurrentFromGraph();
}
} else {
- // Not a load or store.
+ // Not a load, store, or constant.
if (definition->is_used()) {
// Assign fresh SSA temporary and update expression stack.
definition->set_ssa_temp_index(alloc_ssa_temp_index());
@@ -847,6 +851,28 @@
}
+void FlowGraph::RemoveRedefinitions() {
+ // Remove redefinition instructions inserted to inhibit hoisting.
+ for (BlockIterator block_it = reverse_postorder_iterator();
+ !block_it.Done();
+ block_it.Advance()) {
+ for (ForwardInstructionIterator instr_it(block_it.Current());
+ !instr_it.Done();
+ instr_it.Advance()) {
+ RedefinitionInstr* redefinition = instr_it.Current()->AsRedefinition();
+ if (redefinition != NULL) {
+ Definition* original;
+ do {
+ original = redefinition->value()->definition();
+ } while (original->IsRedefinition());
+ redefinition->ReplaceUsesWith(original);
+ instr_it.RemoveCurrentFromGraph();
+ }
+ }
+ }
+}
+
+
// Find the natural loop for the back edge m->n and attach loop information
// to block n (loop header). The algorithm is described in "Advanced Compiler
// Design & Implementation" (Muchnick) p192.
diff --git a/runtime/vm/flow_graph.h b/runtime/vm/flow_graph.h
index 7016767..ec85594 100644
--- a/runtime/vm/flow_graph.h
+++ b/runtime/vm/flow_graph.h
@@ -98,6 +98,7 @@
intptr_t max_block_id() const { return max_block_id_; }
void set_max_block_id(intptr_t id) { max_block_id_ = id; }
+ intptr_t allocate_block_id() { return ++max_block_id_; }
GraphEntryInstr* graph_entry() const {
return graph_entry_;
@@ -111,7 +112,7 @@
intptr_t InstructionCount() const;
- ConstantInstr* AddConstantToInitialDefinitions(const Object& object);
+ ConstantInstr* GetConstant(const Object& object);
void AddToInitialDefinitions(Definition* defn);
void InsertBefore(Instruction* next,
@@ -146,6 +147,26 @@
void ComputeBlockEffects();
BlockEffects* block_effects() const { return block_effects_; }
+ // Remove the redefinition instructions inserted to inhibit code motion.
+ void RemoveRedefinitions();
+
+ // Copy deoptimization target from one instruction to another if we still
+ // have to keep deoptimization environment at gotos for LICM purposes.
+ void CopyDeoptTarget(Instruction* to, Instruction* from) {
+ if (is_licm_allowed()) {
+ to->InheritDeoptTarget(from);
+ }
+ }
+
+ // Returns true if every Goto in the graph is expected to have a
+ // deoptimization environment and can be used as deoptimization target
+ // for hoisted instructions.
+ bool is_licm_allowed() const { return licm_allowed_; }
+
+ // Stop preserving environments on Goto instructions. LICM is not allowed
+ // after this point.
+ void disallow_licm() { licm_allowed_ = false; }
+
private:
friend class IfConverter;
friend class BranchSimplifier;
@@ -201,6 +222,7 @@
ConstantInstr* constant_null_;
BlockEffects* block_effects_;
+ bool licm_allowed_;
};
diff --git a/runtime/vm/flow_graph_allocator.cc b/runtime/vm/flow_graph_allocator.cc
index 5125f97..5c16a8b 100644
--- a/runtime/vm/flow_graph_allocator.cc
+++ b/runtime/vm/flow_graph_allocator.cc
@@ -102,18 +102,6 @@
}
-// Remove environments from the instructions which can't deoptimize.
-void FlowGraphAllocator::EliminateEnvironments() {
- for (intptr_t i = 0; i < block_order_.length(); ++i) {
- BlockEntryInstr* block = block_order_[i];
- for (ForwardInstructionIterator it(block); !it.Done(); it.Advance()) {
- Instruction* current = it.Current();
- if (!current->CanDeoptimize()) current->RemoveEnvironment();
- }
- }
-}
-
-
void SSALivenessAnalysis::ComputeInitialSets() {
const intptr_t block_count = postorder_.length();
for (intptr_t i = 0; i < block_count; i++) {
@@ -152,10 +140,17 @@
for (Environment::DeepIterator env_it(current->env());
!env_it.Done();
env_it.Advance()) {
- Value* value = env_it.CurrentValue();
- if (!value->definition()->IsPushArgument() &&
- !value->BindsToConstant()) {
- live_in->Add(value->definition()->ssa_temp_index());
+ Definition* defn = env_it.CurrentValue()->definition();
+ if (defn->IsMaterializeObject()) {
+ // MaterializeObject instruction is not in the graph.
+ // Treat its inputs as part of the environment.
+ for (intptr_t i = 0; i < defn->InputCount(); i++) {
+ if (!defn->InputAt(i)->BindsToConstant()) {
+ live_in->Add(defn->InputAt(i)->definition()->ssa_temp_index());
+ }
+ }
+ } else if (!defn->IsPushArgument() && !defn->IsConstant()) {
+ live_in->Add(defn->ssa_temp_index());
}
}
}
@@ -742,6 +737,16 @@
continue;
}
+ MaterializeObjectInstr* mat = def->AsMaterializeObject();
+ if (mat != NULL) {
+ // MaterializeObject itself produces no value. But its uses
+ // are treated as part of the environment: allocated locations
+ // will be used when building deoptimization data.
+ locations[i] = Location::NoLocation();
+ ProcessMaterializationUses(block, block_start_pos, use_pos, mat);
+ continue;
+ }
+
const intptr_t vreg = def->ssa_temp_index();
LiveRange* range = GetLiveRange(vreg);
range->AddUseInterval(block_start_pos, use_pos);
@@ -754,6 +759,42 @@
}
+void FlowGraphAllocator::ProcessMaterializationUses(
+ BlockEntryInstr* block,
+ const intptr_t block_start_pos,
+ const intptr_t use_pos,
+ MaterializeObjectInstr* mat) {
+ // Materialization can occur several times in the same environment.
+ // Check if we already processed this one.
+ if (mat->locations() != NULL) {
+ return; // Already processed.
+ }
+
+ // Initialize location for every input of the MaterializeObject instruction.
+ Location* locations =
+ Isolate::Current()->current_zone()->Alloc<Location>(mat->InputCount());
+
+ for (intptr_t i = 0; i < mat->InputCount(); ++i) {
+ Definition* def = mat->InputAt(i)->definition();
+
+ ConstantInstr* constant = def->AsConstant();
+ if (constant != NULL) {
+ locations[i] = Location::Constant(constant->value());
+ continue;
+ }
+
+ locations[i] = Location::Any();
+
+ const intptr_t vreg = def->ssa_temp_index();
+ LiveRange* range = GetLiveRange(vreg);
+ range->AddUseInterval(block_start_pos, use_pos);
+ range->AddUse(use_pos, &locations[i]);
+ }
+
+ mat->set_locations(locations);
+}
+
+
// Create and update live ranges corresponding to instruction's inputs,
// temporaries and output.
void FlowGraphAllocator::ProcessOneInstruction(BlockEntryInstr* block,
@@ -2187,7 +2228,10 @@
for (intptr_t i = unallocated_.length() - 1; i >= 1; i--) {
LiveRange* a = unallocated_[i];
LiveRange* b = unallocated_[i - 1];
- if (!ShouldBeAllocatedBefore(a, b)) return false;
+ if (!ShouldBeAllocatedBefore(a, b)) {
+ UNREACHABLE();
+ return false;
+ }
}
return true;
}
@@ -2432,8 +2476,6 @@
void FlowGraphAllocator::AllocateRegisters() {
CollectRepresentations();
- EliminateEnvironments();
-
liveness_.Analyze();
NumberInstructions();
diff --git a/runtime/vm/flow_graph_allocator.h b/runtime/vm/flow_graph_allocator.h
index 8919e38..0efca0c 100644
--- a/runtime/vm/flow_graph_allocator.h
+++ b/runtime/vm/flow_graph_allocator.h
@@ -66,9 +66,6 @@
private:
void CollectRepresentations();
- // Eliminate unnecessary environments from the IL.
- void EliminateEnvironments();
-
// Visit blocks in the code generation order (reverse post order) and
// linearly assign consequent lifetime positions to every instruction.
// We assign position as follows:
@@ -107,6 +104,10 @@
Instruction* ConnectOutgoingPhiMoves(BlockEntryInstr* block,
BitVector* interference_set);
void ProcessEnvironmentUses(BlockEntryInstr* block, Instruction* current);
+ void ProcessMaterializationUses(BlockEntryInstr* block,
+ const intptr_t block_start_pos,
+ const intptr_t use_pos,
+ MaterializeObjectInstr* mat);
void ProcessOneInstruction(BlockEntryInstr* block,
Instruction* instr,
BitVector* interference_set);
diff --git a/runtime/vm/flow_graph_builder.cc b/runtime/vm/flow_graph_builder.cc
index e341f28..ff98680 100644
--- a/runtime/vm/flow_graph_builder.cc
+++ b/runtime/vm/flow_graph_builder.cc
@@ -97,6 +97,15 @@
}
+void InlineExitCollector::Union(const InlineExitCollector* other) {
+ // It doesn't make sense to combine different calls or calls from
+ // different graphs.
+ ASSERT(caller_graph_ == other->caller_graph_);
+ ASSERT(call_ == other->call_);
+ exits_.AddArray(other->exits_);
+}
+
+
int InlineExitCollector::LowestBlockIdFirst(const Data* a, const Data* b) {
return (a->exit_block->block_id() - b->exit_block->block_id());
}
@@ -133,7 +142,7 @@
caller_graph_->set_max_block_id(join_id);
JoinEntryInstr* join =
new JoinEntryInstr(join_id, CatchClauseNode::kInvalidTryIndex);
- join->InheritDeoptTarget(call_);
+ join->InheritDeoptTargetAfter(call_);
// The dominator set of the join is the intersection of the dominator
// sets of all the predecessors. If we keep the dominator sets ordered
@@ -180,7 +189,8 @@
// We either exhausted the dominators for this block before
// exhausting the current intersection, or else we found a block
// on the path from the root of the tree that is not in common.
- ASSERT(j >= 2);
+ // I.e., there cannot be an empty set of dominators.
+ ASSERT(j > 0);
join_dominators.TruncateTo(j);
break;
}
@@ -189,7 +199,6 @@
}
// The immediate dominator of the join is the last one in the ordered
// intersection.
- join->set_dominator(join_dominators.Last());
join_dominators.Last()->AddDominatedBlock(join);
*exit_block = join;
*last_instruction = join;
@@ -273,7 +282,6 @@
ASSERT(callee_exit->dominated_blocks().is_empty());
for (intptr_t i = 0; i < call_block->dominated_blocks().length(); ++i) {
BlockEntryInstr* block = call_block->dominated_blocks()[i];
- block->set_dominator(callee_exit);
callee_exit->AddDominatedBlock(block);
}
// The call block is now the immediate dominator of blocks whose
@@ -281,10 +289,14 @@
call_block->ClearDominatedBlocks();
for (intptr_t i = 0; i < callee_entry->dominated_blocks().length(); ++i) {
BlockEntryInstr* block = callee_entry->dominated_blocks()[i];
- block->set_dominator(call_block);
call_block->AddDominatedBlock(block);
}
}
+
+ // Neither call nor callee entry are in the graph at this point. Remove them
+ // from use lists.
+ callee_entry->UnuseAllInputs();
+ call_->UnuseAllInputs();
}
@@ -2956,10 +2968,10 @@
const Function& function = owner()->parsed_function().function();
int num_params = function.NumParameters();
int param_frame_index = (num_params == function.num_fixed_parameters()) ?
- (kLastParamSlotIndex + num_params - 1) : kFirstLocalSlotIndex;
+ (kParamEndSlotFromFp + num_params) : kFirstLocalSlotFromFp;
// Handle the saved arguments descriptor as an additional parameter.
if (owner()->parsed_function().GetSavedArgumentsDescriptorVar() != NULL) {
- ASSERT(param_frame_index == kFirstLocalSlotIndex);
+ ASSERT(param_frame_index == kFirstLocalSlotFromFp);
num_params++;
}
for (int pos = 0; pos < num_params; param_frame_index--, pos++) {
diff --git a/runtime/vm/flow_graph_builder.h b/runtime/vm/flow_graph_builder.h
index ec525ec..950224f 100644
--- a/runtime/vm/flow_graph_builder.h
+++ b/runtime/vm/flow_graph_builder.h
@@ -46,6 +46,8 @@
void AddExit(ReturnInstr* exit);
+ void Union(const InlineExitCollector* other);
+
// Before replacing a call with a graph, the outer environment needs to be
// attached to each instruction in the callee graph and the caller graph
// needs to have its block and instruction ID state updated.
diff --git a/runtime/vm/flow_graph_compiler.cc b/runtime/vm/flow_graph_compiler.cc
index 2ec6ebc..222501c 100644
--- a/runtime/vm/flow_graph_compiler.cc
+++ b/runtime/vm/flow_graph_compiler.cc
@@ -41,14 +41,27 @@
if (env == NULL) return;
AllocateIncomingParametersRecursive(env->outer(), stack_height);
for (Environment::ShallowIterator it(env); !it.Done(); it.Advance()) {
- if (it.CurrentLocation().IsInvalid()) {
- ASSERT(it.CurrentValue()->definition()->IsPushArgument());
+ if (it.CurrentLocation().IsInvalid() &&
+ it.CurrentValue()->definition()->IsPushArgument()) {
it.SetCurrentLocation(Location::StackSlot((*stack_height)++));
}
}
}
+void CompilerDeoptInfo::EmitMaterializations(Environment* env,
+ DeoptInfoBuilder* builder) {
+ for (Environment::DeepIterator it(env); !it.Done(); it.Advance()) {
+ if (it.CurrentLocation().IsInvalid()) {
+ MaterializeObjectInstr* mat =
+ it.CurrentValue()->definition()->AsMaterializeObject();
+ ASSERT(mat != NULL);
+ builder->AddMaterialization(mat);
+ }
+ }
+}
+
+
RawDeoptInfo* CompilerDeoptInfo::CreateDeoptInfo(FlowGraphCompiler* compiler,
DeoptInfoBuilder* builder) {
if (deoptimization_env_ == NULL) return DeoptInfo::null();
@@ -59,10 +72,21 @@
intptr_t slot_ix = 0;
Environment* current = deoptimization_env_;
+ // Emit all kMaterializeObject instructions describing objects to be
+ // materialized on the deoptimization as a prefix to the deoptimization info.
+ EmitMaterializations(deoptimization_env_, builder);
+
+ // The real frame starts here.
+ builder->MarkFrameStart();
builder->AddReturnAddress(current->function(),
deopt_id(),
slot_ix++);
+ // Emit all values that are needed for materialization as a part of the
+ // expression stack for the bottom-most frame. This guarantees that GC
+ // will be able to find them during materialization.
+ slot_ix = builder->EmitMaterializationArguments();
+
// For the innermost environment, set outgoing arguments and the locals.
for (intptr_t i = current->Length() - 1;
i >= current->fixed_parameter_count();
@@ -1125,6 +1149,12 @@
}
+static int HighestCountFirst(const CidTarget* a, const CidTarget* b) {
+ // Negative if 'a' should sort before 'b'.
+ return b->count - a->count;
+}
+
+
// Returns 'sorted' array in decreasing count order.
// The expected number of elements to sort is less than 10.
void FlowGraphCompiler::SortICDataByCount(const ICData& ic_data,
@@ -1138,20 +1168,7 @@
&Function::ZoneHandle(ic_data.GetTargetAt(i)),
ic_data.GetCountAt(i)));
}
- for (int i = 0; i < len; i++) {
- intptr_t largest_ix = i;
- for (int k = i + 1; k < len; k++) {
- if ((*sorted)[largest_ix].count < (*sorted)[k].count) {
- largest_ix = k;
- }
- }
- if (i != largest_ix) {
- // Swap.
- CidTarget temp = (*sorted)[i];
- (*sorted)[i] = (*sorted)[largest_ix];
- (*sorted)[largest_ix] = temp;
- }
- }
+ sorted->Sort(HighestCountFirst);
}
} // namespace dart
diff --git a/runtime/vm/flow_graph_compiler.h b/runtime/vm/flow_graph_compiler.h
index 4c7204a..ffdf981 100644
--- a/runtime/vm/flow_graph_compiler.h
+++ b/runtime/vm/flow_graph_compiler.h
@@ -116,8 +116,6 @@
RawDeoptInfo* CreateDeoptInfo(FlowGraphCompiler* compiler,
DeoptInfoBuilder* builder);
- void AllocateIncomingParametersRecursive(Environment* env,
- intptr_t* stack_height);
// No code needs to be generated.
virtual void GenerateCode(FlowGraphCompiler* compiler, intptr_t stub_ix) {}
@@ -133,6 +131,11 @@
void set_deoptimization_env(Environment* env) { deoptimization_env_ = env; }
private:
+ void EmitMaterializations(Environment* env, DeoptInfoBuilder* builder);
+
+ void AllocateIncomingParametersRecursive(Environment* env,
+ intptr_t* stack_height);
+
intptr_t pc_offset_;
const intptr_t deopt_id_;
const DeoptReasonId reason_;
@@ -452,6 +455,10 @@
Register array,
Register index);
+ // Returns 'sorted' array in decreasing count order.
+ static void SortICDataByCount(const ICData& ic_data,
+ GrowableArray<CidTarget>* sorted);
+
private:
friend class CheckStackOverflowSlowPath; // For pending_deoptimization_env_.
@@ -541,11 +548,6 @@
return block_order_.length() - index - 1;
}
- // Returns 'sorted' array in decreasing count order.
- // The expected number of elements to sort is less than 10.
- static void SortICDataByCount(const ICData& ic_data,
- GrowableArray<CidTarget>* sorted);
-
void CompactBlock(BlockEntryInstr* block);
void CompactBlocks();
diff --git a/runtime/vm/flow_graph_compiler_arm.cc b/runtime/vm/flow_graph_compiler_arm.cc
index e88a8f7..6d32025 100644
--- a/runtime/vm/flow_graph_compiler_arm.cc
+++ b/runtime/vm/flow_graph_compiler_arm.cc
@@ -575,7 +575,7 @@
const int num_params =
num_fixed_params + num_opt_pos_params + num_opt_named_params;
ASSERT(function.NumParameters() == num_params);
- ASSERT(parsed_function().first_parameter_index() == kFirstLocalSlotIndex);
+ ASSERT(parsed_function().first_parameter_index() == kFirstLocalSlotFromFp);
// Check that min_num_pos_args <= num_pos_args <= max_num_pos_args,
// where num_pos_args is the number of positional arguments passed in.
@@ -592,20 +592,20 @@
__ b(&wrong_num_arguments, GT);
// Copy positional arguments.
- // Argument i passed at fp[kLastParamSlotIndex + num_args - 1 - i] is copied
- // to fp[kFirstLocalSlotIndex - i].
+ // Argument i passed at fp[kParamEndSlotFromFp + num_args - i] is copied
+ // to fp[kFirstLocalSlotFromFp - i].
__ ldr(R7, FieldAddress(R4, ArgumentsDescriptor::count_offset()));
// Since R7 and R8 are Smi, use LSL 1 instead of LSL 2.
// Let R7 point to the last passed positional argument, i.e. to
- // fp[kLastParamSlotIndex + num_args - 1 - (num_pos_args - 1)].
+ // fp[kParamEndSlotFromFp + num_args - (num_pos_args - 1)].
__ sub(R7, R7, ShifterOperand(R8));
__ add(R7, FP, ShifterOperand(R7, LSL, 1));
- __ add(R7, R7, ShifterOperand(kLastParamSlotIndex * kWordSize));
+ __ add(R7, R7, ShifterOperand((kParamEndSlotFromFp + 1) * kWordSize));
// Let R6 point to the last copied positional argument, i.e. to
- // fp[kFirstLocalSlotIndex - (num_pos_args - 1)].
- __ AddImmediate(R6, FP, (kFirstLocalSlotIndex + 1) * kWordSize);
+ // fp[kFirstLocalSlotFromFp - (num_pos_args - 1)].
+ __ AddImmediate(R6, FP, (kFirstLocalSlotFromFp + 1) * kWordSize);
__ sub(R6, R6, ShifterOperand(R8, LSL, 1)); // R8 is a Smi.
__ SmiUntag(R8);
Label loop, loop_condition;
@@ -649,9 +649,9 @@
FieldAddress(R4, ArgumentsDescriptor::positional_count_offset()));
__ SmiUntag(R8);
// Let R7 point to the first passed argument, i.e. to
- // fp[kLastParamSlotIndex + num_args - 1 - 0]; num_args (R7) is Smi.
+ // fp[kParamEndSlotFromFp + num_args - 0]; num_args (R7) is Smi.
__ add(R7, FP, ShifterOperand(R7, LSL, 1));
- __ AddImmediate(R7, R7, (kLastParamSlotIndex - 1) * kWordSize);
+ __ AddImmediate(R7, R7, kParamEndSlotFromFp * kWordSize);
// Let R6 point to the entry of the first named argument.
__ add(R6, R4, ShifterOperand(
ArgumentsDescriptor::first_named_entry_offset() - kHeapObjectTag));
@@ -665,7 +665,7 @@
__ CompareObject(R5, opt_param[i]->name());
__ b(&load_default_value, NE);
// Load R5 with passed-in argument at provided arg_pos, i.e. at
- // fp[kLastParamSlotIndex + num_args - 1 - arg_pos].
+ // fp[kParamEndSlotFromFp + num_args - arg_pos].
__ ldr(R5, Address(R6, ArgumentsDescriptor::position_offset()));
// R5 is arg_pos as Smi.
// Point to next named entry.
@@ -681,11 +681,11 @@
param_pos - num_fixed_params));
__ LoadObject(R5, value);
__ Bind(&assign_optional_parameter);
- // Assign R5 to fp[kFirstLocalSlotIndex - param_pos].
+ // Assign R5 to fp[kFirstLocalSlotFromFp - param_pos].
// We do not use the final allocation index of the variable here, i.e.
// scope->VariableAt(i)->index(), because captured variables still need
// to be copied to the context that is not yet allocated.
- const intptr_t computed_param_pos = kFirstLocalSlotIndex - param_pos;
+ const intptr_t computed_param_pos = kFirstLocalSlotFromFp - param_pos;
const Address param_addr(FP, computed_param_pos * kWordSize);
__ str(R5, param_addr);
}
@@ -712,11 +712,11 @@
const Object& value = Object::ZoneHandle(
parsed_function().default_parameter_values().At(i));
__ LoadObject(R5, value);
- // Assign R5 to fp[kFirstLocalSlotIndex - param_pos].
+ // Assign R5 to fp[kFirstLocalSlotFromFp - param_pos].
// We do not use the final allocation index of the variable here, i.e.
// scope->VariableAt(i)->index(), because captured variables still need
// to be copied to the context that is not yet allocated.
- const intptr_t computed_param_pos = kFirstLocalSlotIndex - param_pos;
+ const intptr_t computed_param_pos = kFirstLocalSlotFromFp - param_pos;
const Address param_addr(FP, computed_param_pos * kWordSize);
__ str(R5, param_addr);
__ Bind(&next_parameter);
@@ -778,7 +778,7 @@
// R4 : arguments descriptor array.
__ ldr(R8, FieldAddress(R4, ArgumentsDescriptor::count_offset()));
__ SmiUntag(R8);
- __ add(R7, FP, ShifterOperand(kLastParamSlotIndex * kWordSize));
+ __ add(R7, FP, ShifterOperand((kParamEndSlotFromFp + 1) * kWordSize));
const Address original_argument_addr(R7, R8, LSL, 2);
__ LoadImmediate(IP, reinterpret_cast<intptr_t>(Object::null()));
Label null_args_loop, null_args_loop_condition;
diff --git a/runtime/vm/flow_graph_compiler_ia32.cc b/runtime/vm/flow_graph_compiler_ia32.cc
index e609740..13c4965 100644
--- a/runtime/vm/flow_graph_compiler_ia32.cc
+++ b/runtime/vm/flow_graph_compiler_ia32.cc
@@ -669,7 +669,7 @@
const int num_params =
num_fixed_params + num_opt_pos_params + num_opt_named_params;
ASSERT(function.NumParameters() == num_params);
- ASSERT(parsed_function().first_parameter_index() == kFirstLocalSlotIndex);
+ ASSERT(parsed_function().first_parameter_index() == kFirstLocalSlotFromFp);
// Check that min_num_pos_args <= num_pos_args <= max_num_pos_args,
// where num_pos_args is the number of positional arguments passed in.
@@ -687,19 +687,20 @@
__ j(GREATER, &wrong_num_arguments);
// Copy positional arguments.
- // Argument i passed at fp[kLastParamSlotIndex + num_args - 1 - i] is copied
- // to fp[kFirstLocalSlotIndex - i].
+ // Argument i passed at fp[kParamEndSlotFromFp + num_args - i] is copied
+ // to fp[kFirstLocalSlotFromFp - i].
__ movl(EBX, FieldAddress(EDX, ArgumentsDescriptor::count_offset()));
// Since EBX and ECX are Smi, use TIMES_2 instead of TIMES_4.
// Let EBX point to the last passed positional argument, i.e. to
- // fp[kLastParamSlotIndex + num_args - 1 - (num_pos_args - 1)].
+ // fp[kParamEndSlotFromFp + num_args - (num_pos_args - 1)].
__ subl(EBX, ECX);
- __ leal(EBX, Address(EBP, EBX, TIMES_2, kLastParamSlotIndex * kWordSize));
+ __ leal(EBX, Address(EBP, EBX, TIMES_2,
+ (kParamEndSlotFromFp + 1) * kWordSize));
// Let EDI point to the last copied positional argument, i.e. to
- // fp[kFirstLocalSlotIndex - (num_pos_args - 1)].
- __ leal(EDI, Address(EBP, (kFirstLocalSlotIndex + 1) * kWordSize));
+ // fp[kFirstLocalSlotFromFp - (num_pos_args - 1)].
+ __ leal(EDI, Address(EBP, (kFirstLocalSlotFromFp + 1) * kWordSize));
__ subl(EDI, ECX); // ECX is a Smi, subtract twice for TIMES_4 scaling.
__ subl(EDI, ECX);
__ SmiUntag(ECX);
@@ -746,9 +747,9 @@
FieldAddress(EDX, ArgumentsDescriptor::positional_count_offset()));
__ SmiUntag(ECX);
// Let EBX point to the first passed argument, i.e. to
- // fp[kLastParamSlotIndex + num_args - 1 - 0]; num_args (EBX) is Smi.
+ // fp[kParamEndSlotFromFp + num_args - 0]; num_args (EBX) is Smi.
__ leal(EBX,
- Address(EBP, EBX, TIMES_2, (kLastParamSlotIndex - 1) * kWordSize));
+ Address(EBP, EBX, TIMES_2, kParamEndSlotFromFp * kWordSize));
// Let EDI point to the entry of the first named argument.
__ leal(EDI,
FieldAddress(EDX, ArgumentsDescriptor::first_named_entry_offset()));
@@ -762,7 +763,7 @@
__ CompareObject(EAX, opt_param[i]->name());
__ j(NOT_EQUAL, &load_default_value, Assembler::kNearJump);
// Load EAX with passed-in argument at provided arg_pos, i.e. at
- // fp[kLastParamSlotIndex + num_args - 1 - arg_pos].
+ // fp[kParamEndSlotFromFp + num_args - arg_pos].
__ movl(EAX, Address(EDI, ArgumentsDescriptor::position_offset()));
// EAX is arg_pos as Smi.
// Point to next named entry.
@@ -778,11 +779,11 @@
param_pos - num_fixed_params));
__ LoadObject(EAX, value);
__ Bind(&assign_optional_parameter);
- // Assign EAX to fp[kFirstLocalSlotIndex - param_pos].
+ // Assign EAX to fp[kFirstLocalSlotFromFp - param_pos].
// We do not use the final allocation index of the variable here, i.e.
// scope->VariableAt(i)->index(), because captured variables still need
// to be copied to the context that is not yet allocated.
- const intptr_t computed_param_pos = kFirstLocalSlotIndex - param_pos;
+ const intptr_t computed_param_pos = kFirstLocalSlotFromFp - param_pos;
const Address param_addr(EBP, computed_param_pos * kWordSize);
__ movl(param_addr, EAX);
}
@@ -808,11 +809,11 @@
const Object& value = Object::ZoneHandle(
parsed_function().default_parameter_values().At(i));
__ LoadObject(EAX, value);
- // Assign EAX to fp[kFirstLocalSlotIndex - param_pos].
+ // Assign EAX to fp[kFirstLocalSlotFromFp - param_pos].
// We do not use the final allocation index of the variable here, i.e.
// scope->VariableAt(i)->index(), because captured variables still need
// to be copied to the context that is not yet allocated.
- const intptr_t computed_param_pos = kFirstLocalSlotIndex - param_pos;
+ const intptr_t computed_param_pos = kFirstLocalSlotFromFp - param_pos;
const Address param_addr(EBP, computed_param_pos * kWordSize);
__ movl(param_addr, EAX);
__ Bind(&next_parameter);
@@ -875,7 +876,7 @@
Label null_args_loop, null_args_loop_condition;
__ jmp(&null_args_loop_condition, Assembler::kNearJump);
const Address original_argument_addr(
- EBP, ECX, TIMES_4, kLastParamSlotIndex * kWordSize);
+ EBP, ECX, TIMES_4, (kParamEndSlotFromFp + 1) * kWordSize);
__ Bind(&null_args_loop);
__ movl(original_argument_addr, raw_null);
__ Bind(&null_args_loop_condition);
diff --git a/runtime/vm/flow_graph_compiler_mips.cc b/runtime/vm/flow_graph_compiler_mips.cc
index 466846f..7729e57 100644
--- a/runtime/vm/flow_graph_compiler_mips.cc
+++ b/runtime/vm/flow_graph_compiler_mips.cc
@@ -598,7 +598,7 @@
const int num_params =
num_fixed_params + num_opt_pos_params + num_opt_named_params;
ASSERT(function.NumParameters() == num_params);
- ASSERT(parsed_function().first_parameter_index() == kFirstLocalSlotIndex);
+ ASSERT(parsed_function().first_parameter_index() == kFirstLocalSlotFromFp);
// Check that min_num_pos_args <= num_pos_args <= max_num_pos_args,
// where num_pos_args is the number of positional arguments passed in.
@@ -616,21 +616,21 @@
&wrong_num_arguments);
// Copy positional arguments.
- // Argument i passed at fp[kLastParamSlotIndex + num_args - 1 - i] is copied
- // to fp[kFirstLocalSlotIndex - i].
+ // Argument i passed at fp[kParamEndSlotFromFp + num_args - i] is copied
+ // to fp[kFirstLocalSlotFromFp - i].
__ lw(T1, FieldAddress(S4, ArgumentsDescriptor::count_offset()));
// Since T1 and T2 are Smi, use sll 1 instead of sll 2.
// Let T1 point to the last passed positional argument, i.e. to
- // fp[kLastParamSlotIndex + num_args - 1 - (num_pos_args - 1)].
+ // fp[kParamEndSlotFromFp + num_args - (num_pos_args - 1)].
__ subu(T1, T1, T2);
__ sll(T1, T1, 1);
__ addu(T1, FP, T1);
- __ AddImmediate(T1, kLastParamSlotIndex * kWordSize);
+ __ AddImmediate(T1, (kParamEndSlotFromFp + 1) * kWordSize);
// Let T0 point to the last copied positional argument, i.e. to
- // fp[kFirstLocalSlotIndex - (num_pos_args - 1)].
- __ AddImmediate(T0, FP, (kFirstLocalSlotIndex + 1) * kWordSize);
+ // fp[kFirstLocalSlotFromFp - (num_pos_args - 1)].
+ __ AddImmediate(T0, FP, (kFirstLocalSlotFromFp + 1) * kWordSize);
__ sll(T2, T2, 1); // T2 is a Smi.
Label loop, loop_exit;
@@ -671,10 +671,10 @@
__ lw(T2, FieldAddress(S4, ArgumentsDescriptor::positional_count_offset()));
__ SmiUntag(T2);
// Let T1 point to the first passed argument, i.e. to
- // fp[kLastParamSlotIndex + num_args - 1 - 0]; num_args (T1) is Smi.
+ // fp[kParamEndSlotFromFp + num_args - 0]; num_args (T1) is Smi.
__ sll(T3, T1, 1);
__ addu(T1, FP, T3);
- __ AddImmediate(T1, (kLastParamSlotIndex - 1) * kWordSize);
+ __ AddImmediate(T1, kParamEndSlotFromFp * kWordSize);
// Let T0 point to the entry of the first named argument.
__ AddImmediate(T0, S4,
ArgumentsDescriptor::first_named_entry_offset() - kHeapObjectTag);
@@ -688,7 +688,7 @@
__ BranchNotEqual(T3, opt_param[i]->name(), &load_default_value);
// Load T3 with passed-in argument at provided arg_pos, i.e. at
- // fp[kLastParamSlotIndex + num_args - 1 - arg_pos].
+ // fp[kParamEndSlotFromFp + num_args - arg_pos].
__ lw(T3, Address(T0, ArgumentsDescriptor::position_offset()));
// T3 is arg_pos as Smi.
// Point to next named entry.
@@ -706,11 +706,11 @@
param_pos - num_fixed_params));
__ LoadObject(T3, value);
__ Bind(&assign_optional_parameter);
- // Assign T3 to fp[kFirstLocalSlotIndex - param_pos].
+ // Assign T3 to fp[kFirstLocalSlotFromFp - param_pos].
// We do not use the final allocation index of the variable here, i.e.
// scope->VariableAt(i)->index(), because captured variables still need
// to be copied to the context that is not yet allocated.
- const intptr_t computed_param_pos = kFirstLocalSlotIndex - param_pos;
+ const intptr_t computed_param_pos = kFirstLocalSlotFromFp - param_pos;
__ sw(T3, Address(FP, computed_param_pos * kWordSize));
}
delete[] opt_param;
@@ -735,11 +735,11 @@
const Object& value = Object::ZoneHandle(
parsed_function().default_parameter_values().At(i));
__ LoadObject(T3, value);
- // Assign T3 to fp[kFirstLocalSlotIndex - param_pos].
+ // Assign T3 to fp[kFirstLocalSlotFromFp - param_pos].
// We do not use the final allocation index of the variable here, i.e.
// scope->VariableAt(i)->index(), because captured variables still need
// to be copied to the context that is not yet allocated.
- const intptr_t computed_param_pos = kFirstLocalSlotIndex - param_pos;
+ const intptr_t computed_param_pos = kFirstLocalSlotFromFp - param_pos;
__ sw(T3, Address(FP, computed_param_pos * kWordSize));
__ Bind(&next_parameter);
}
@@ -803,7 +803,8 @@
Label null_args_loop, null_args_loop_exit;
__ blez(T2, &null_args_loop_exit);
- __ delay_slot()->addiu(T1, FP, Immediate(kLastParamSlotIndex * kWordSize));
+ __ delay_slot()->addiu(T1, FP,
+ Immediate((kParamEndSlotFromFp + 1) * kWordSize));
__ Bind(&null_args_loop);
__ addiu(T2, T2, Immediate(-kWordSize));
__ addu(T3, T1, T2);
diff --git a/runtime/vm/flow_graph_compiler_x64.cc b/runtime/vm/flow_graph_compiler_x64.cc
index 400ab4e..70074ca 100644
--- a/runtime/vm/flow_graph_compiler_x64.cc
+++ b/runtime/vm/flow_graph_compiler_x64.cc
@@ -665,7 +665,7 @@
const int num_params =
num_fixed_params + num_opt_pos_params + num_opt_named_params;
ASSERT(function.NumParameters() == num_params);
- ASSERT(parsed_function().first_parameter_index() == kFirstLocalSlotIndex);
+ ASSERT(parsed_function().first_parameter_index() == kFirstLocalSlotFromFp);
// Check that min_num_pos_args <= num_pos_args <= max_num_pos_args,
// where num_pos_args is the number of positional arguments passed in.
@@ -683,24 +683,25 @@
__ j(GREATER, &wrong_num_arguments);
// Copy positional arguments.
- // Argument i passed at fp[kLastParamSlotIndex + num_args - 1 - i] is copied
- // to fp[kFirstLocalSlotIndex - i].
+ // Argument i passed at fp[kParamEndSlotFromFp + num_args - i] is copied
+ // to fp[kFirstLocalSlotFromFp - i].
__ movq(RBX, FieldAddress(R10, ArgumentsDescriptor::count_offset()));
// Since RBX and RCX are Smi, use TIMES_4 instead of TIMES_8.
// Let RBX point to the last passed positional argument, i.e. to
- // fp[kLastParamSlotIndex + num_args - 1 - (num_pos_args - 1)].
+ // fp[kParamEndSlotFromFp + num_args - (num_pos_args - 1)].
__ subq(RBX, RCX);
- __ leaq(RBX, Address(RBP, RBX, TIMES_4, kLastParamSlotIndex * kWordSize));
+ __ leaq(RBX, Address(RBP, RBX, TIMES_4,
+ (kParamEndSlotFromFp + 1) * kWordSize));
// Let RDI point to the last copied positional argument, i.e. to
- // fp[kFirstLocalSlotIndex - (num_pos_args - 1)].
+ // fp[kFirstLocalSlotFromFp - (num_pos_args - 1)].
__ SmiUntag(RCX);
__ movq(RAX, RCX);
__ negq(RAX);
// -num_pos_args is in RAX.
__ leaq(RDI,
- Address(RBP, RAX, TIMES_8, (kFirstLocalSlotIndex + 1) * kWordSize));
+ Address(RBP, RAX, TIMES_8, (kFirstLocalSlotFromFp + 1) * kWordSize));
Label loop, loop_condition;
__ jmp(&loop_condition, Assembler::kNearJump);
// We do not use the final allocation index of the variable here, i.e.
@@ -744,9 +745,9 @@
FieldAddress(R10, ArgumentsDescriptor::positional_count_offset()));
__ SmiUntag(RCX);
// Let RBX point to the first passed argument, i.e. to
- // fp[kLastParamSlotIndex + num_args - 1]; num_args (RBX) is Smi.
+ // fp[kParamEndSlotFromFp + num_args]; num_args (RBX) is Smi.
__ leaq(RBX,
- Address(RBP, RBX, TIMES_4, (kLastParamSlotIndex - 1) * kWordSize));
+ Address(RBP, RBX, TIMES_4, kParamEndSlotFromFp * kWordSize));
// Let RDI point to the entry of the first named argument.
__ leaq(RDI,
FieldAddress(R10, ArgumentsDescriptor::first_named_entry_offset()));
@@ -760,7 +761,7 @@
__ CompareObject(RAX, opt_param[i]->name());
__ j(NOT_EQUAL, &load_default_value, Assembler::kNearJump);
// Load RAX with passed-in argument at provided arg_pos, i.e. at
- // fp[kLastParamSlotIndex + num_args - 1 - arg_pos].
+ // fp[kParamEndSlotFromFp + num_args - arg_pos].
__ movq(RAX, Address(RDI, ArgumentsDescriptor::position_offset()));
// RAX is arg_pos as Smi.
// Point to next named entry.
@@ -776,11 +777,11 @@
param_pos - num_fixed_params));
__ LoadObject(RAX, value);
__ Bind(&assign_optional_parameter);
- // Assign RAX to fp[kFirstLocalSlotIndex - param_pos].
+ // Assign RAX to fp[kFirstLocalSlotFromFp - param_pos].
// We do not use the final allocation index of the variable here, i.e.
// scope->VariableAt(i)->index(), because captured variables still need
// to be copied to the context that is not yet allocated.
- const intptr_t computed_param_pos = kFirstLocalSlotIndex - param_pos;
+ const intptr_t computed_param_pos = kFirstLocalSlotFromFp - param_pos;
const Address param_addr(RBP, computed_param_pos * kWordSize);
__ movq(param_addr, RAX);
}
@@ -806,11 +807,11 @@
const Object& value = Object::ZoneHandle(
parsed_function().default_parameter_values().At(i));
__ LoadObject(RAX, value);
- // Assign RAX to fp[kFirstLocalSlotIndex - param_pos].
+ // Assign RAX to fp[kFirstLocalSlotFromFp - param_pos].
// We do not use the final allocation index of the variable here, i.e.
// scope->VariableAt(i)->index(), because captured variables still need
// to be copied to the context that is not yet allocated.
- const intptr_t computed_param_pos = kFirstLocalSlotIndex - param_pos;
+ const intptr_t computed_param_pos = kFirstLocalSlotFromFp - param_pos;
const Address param_addr(RBP, computed_param_pos * kWordSize);
__ movq(param_addr, RAX);
__ Bind(&next_parameter);
@@ -873,7 +874,7 @@
Label null_args_loop, null_args_loop_condition;
__ jmp(&null_args_loop_condition, Assembler::kNearJump);
const Address original_argument_addr(
- RBP, RCX, TIMES_8, kLastParamSlotIndex * kWordSize);
+ RBP, RCX, TIMES_8, (kParamEndSlotFromFp + 1) * kWordSize);
__ Bind(&null_args_loop);
__ movq(original_argument_addr, raw_null);
__ Bind(&null_args_loop_condition);
diff --git a/runtime/vm/flow_graph_inliner.cc b/runtime/vm/flow_graph_inliner.cc
index af3f6e2..f57251c 100644
--- a/runtime/vm/flow_graph_inliner.cc
+++ b/runtime/vm/flow_graph_inliner.cc
@@ -8,6 +8,7 @@
#include "vm/flags.h"
#include "vm/flow_graph.h"
#include "vm/flow_graph_builder.h"
+#include "vm/flow_graph_compiler.h"
#include "vm/flow_graph_optimizer.h"
#include "vm/il_printer.h"
#include "vm/intrinsifier.h"
@@ -135,7 +136,6 @@
// Pair of an argument name and its value.
struct NamedArgument {
- public:
String* name;
Value* value;
NamedArgument(String* name, Value* value)
@@ -290,6 +290,51 @@
};
+struct InlinedCallData {
+ InlinedCallData(Definition* call, GrowableArray<Value*>* arguments)
+ : call(call),
+ arguments(arguments),
+ callee_graph(NULL),
+ parameter_stubs(NULL),
+ exit_collector(NULL) { }
+
+ Definition* call;
+ GrowableArray<Value*>* arguments;
+ FlowGraph* callee_graph;
+ ZoneGrowableArray<Definition*>* parameter_stubs;
+ InlineExitCollector* exit_collector;
+};
+
+
+class CallSiteInliner;
+
+class PolymorphicInliner : public ValueObject {
+ public:
+ PolymorphicInliner(CallSiteInliner* owner,
+ PolymorphicInstanceCallInstr* call);
+
+ void Inline();
+
+ private:
+ bool CheckInlinedDuplicate(const Function& target);
+ bool CheckNonInlinedDuplicate(const Function& target);
+
+ bool TryInlining(const Function& target);
+
+ TargetEntryInstr* BuildDecisionGraph();
+
+ CallSiteInliner* const owner_;
+ PolymorphicInstanceCallInstr* const call_;
+ const intptr_t num_variants_;
+ GrowableArray<CidTarget> variants_;
+
+ GrowableArray<CidTarget> inlined_variants_;
+ GrowableArray<CidTarget> non_inlined_variants_;
+ GrowableArray<BlockEntryInstr*> inlined_entries_;
+ InlineExitCollector* exit_collector_;
+};
+
+
class CallSiteInliner : public ValueObject {
public:
CallSiteInliner(FlowGraph* flow_graph,
@@ -304,6 +349,8 @@
function_cache_(),
guarded_fields_(guarded_fields) { }
+ FlowGraph* caller_graph() const { return caller_graph_; }
+
// Inlining heuristics based on Cooper et al. 2008.
bool ShouldWeInline(intptr_t instr_count,
intptr_t call_site_count,
@@ -359,23 +406,6 @@
static_cast<double>(initial_size_);
}
- private:
- struct InlinedCallData {
- public:
- InlinedCallData(Definition* call, GrowableArray<Value*>* arguments)
- : call(call),
- arguments(arguments),
- callee_graph(NULL),
- parameter_stubs(NULL),
- exit_collector(NULL) { }
-
- Definition* call;
- GrowableArray<Value*>* arguments;
- FlowGraph* callee_graph;
- ZoneGrowableArray<Definition*>* parameter_stubs;
- InlineExitCollector* exit_collector;
- };
-
bool TryInlining(const Function& function,
const Array& argument_names,
InlinedCallData* call_data) {
@@ -603,6 +633,7 @@
}
}
+ private:
void InlineCall(InlinedCallData* call_data) {
TimerScope timer(FLAG_compiler_stats,
&CompilerStats::graphinliner_subst_timer,
@@ -639,8 +670,7 @@
ConstantInstr* constant = (*defns)[i]->AsConstant();
if ((constant != NULL) && constant->HasUses()) {
constant->ReplaceUsesWith(
- caller_graph_->AddConstantToInitialDefinitions(
- constant->value()));
+ caller_graph_->GetConstant(constant->value()));
}
}
@@ -739,24 +769,22 @@
inlining_call_sites_->instance_calls();
TRACE_INLINING(OS::Print(" Polymorphic Instance Calls (%d)\n",
call_info.length()));
- for (intptr_t i = 0; i < call_info.length(); ++i) {
- PolymorphicInstanceCallInstr* call = call_info[i].call;
- const ICData& ic_data = call->ic_data();
- const Function& target = Function::ZoneHandle(ic_data.GetTargetAt(0));
+ for (intptr_t call_idx = 0; call_idx < call_info.length(); ++call_idx) {
+ PolymorphicInstanceCallInstr* call = call_info[call_idx].call;
if (call->with_checks()) {
- TRACE_INLINING(OS::Print(
- " => %s (deopt count %d)\n Bailout: %"Pd" checks\n",
- target.ToCString(),
- target.deoptimization_counter(),
- ic_data.NumberOfChecks()));
+ PolymorphicInliner inliner(this, call);
+ inliner.Inline();
continue;
}
- if ((call_info[i].ratio * 100) < FLAG_inlining_hotness) {
+
+ const ICData& ic_data = call->ic_data();
+ const Function& target = Function::ZoneHandle(ic_data.GetTargetAt(0));
+ if ((call_info[call_idx].ratio * 100) < FLAG_inlining_hotness) {
TRACE_INLINING(OS::Print(
" => %s (deopt count %d)\n Bailout: cold %f\n",
target.ToCString(),
target.deoptimization_counter(),
- call_info[i].ratio));
+ call_info[call_idx].ratio));
continue;
}
GrowableArray<Value*> arguments(call->ArgumentCount());
@@ -876,6 +904,383 @@
};
+PolymorphicInliner::PolymorphicInliner(CallSiteInliner* owner,
+ PolymorphicInstanceCallInstr* call)
+ : owner_(owner),
+ call_(call),
+ num_variants_(call->ic_data().NumberOfChecks()),
+ variants_(num_variants_),
+ inlined_variants_(num_variants_),
+ non_inlined_variants_(num_variants_),
+ inlined_entries_(num_variants_),
+ exit_collector_(new InlineExitCollector(owner->caller_graph(), call)) {
+}
+
+
+// Inlined bodies are shared if two different class ids have the same
+// inlined target. This sharing is represented by using three different
+// types of entries in the inlined_entries_ array:
+//
+// * GraphEntry: the inlined body is not shared.
+//
+// * TargetEntry: the inlined body is shared and this is the first variant.
+//
+// * JoinEntry: the inlined body is shared and this is a subsequent variant.
+bool PolymorphicInliner::CheckInlinedDuplicate(const Function& target) {
+ for (intptr_t i = 0; i < inlined_variants_.length(); ++i) {
+ if (target.raw() == inlined_variants_[i].target->raw()) {
+ // The call target is shared with a previous inlined variant. Share
+ // the graph. This requires a join block at the entry, and edge-split
+ // form requires a target for each branch.
+ //
+ // Represent the sharing by recording a fresh target for the first
+ // variant and the shared join for all later variants.
+ if (inlined_entries_[i]->IsGraphEntry()) {
+ // Convert the old target entry to a new join entry.
+ TargetEntryInstr* old_target =
+ inlined_entries_[i]->AsGraphEntry()->normal_entry();
+ JoinEntryInstr* new_join = BranchSimplifier::ToJoinEntry(old_target);
+ old_target->ReplaceAsPredecessorWith(new_join);
+ for (intptr_t j = 0; j < old_target->dominated_blocks().length(); ++j) {
+ BlockEntryInstr* block = old_target->dominated_blocks()[j];
+ new_join->AddDominatedBlock(block);
+ }
+ // Create a new target with the join as unconditional successor.
+ TargetEntryInstr* new_target =
+ new TargetEntryInstr(owner_->caller_graph()->allocate_block_id(),
+ old_target->try_index());
+ new_target->InheritDeoptTarget(new_join);
+ GotoInstr* new_goto = new GotoInstr(new_join);
+ new_goto->InheritDeoptTarget(new_join);
+ new_target->LinkTo(new_goto);
+ new_target->set_last_instruction(new_goto);
+ new_join->predecessors_.Add(new_target);
+
+ // Record the new target for the first variant.
+ inlined_entries_[i] = new_target;
+ }
+ ASSERT(inlined_entries_[i]->IsTargetEntry());
+ // Record the shared join for this variant.
+ BlockEntryInstr* join =
+ inlined_entries_[i]->last_instruction()->SuccessorAt(0);
+ ASSERT(join->IsJoinEntry());
+ inlined_entries_.Add(join);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+bool PolymorphicInliner::CheckNonInlinedDuplicate(const Function& target) {
+ for (intptr_t i = 0; i < non_inlined_variants_.length(); ++i) {
+ if (target.raw() == non_inlined_variants_[i].target->raw()) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+bool PolymorphicInliner::TryInlining(const Function& target) {
+ GrowableArray<Value*> arguments(call_->ArgumentCount());
+ for (int i = 0; i < call_->ArgumentCount(); ++i) {
+ arguments.Add(call_->PushArgumentAt(i)->value());
+ }
+ InlinedCallData call_data(call_, &arguments);
+ if (!owner_->TryInlining(target,
+ call_->instance_call()->argument_names(),
+ &call_data)) {
+ return false;
+ }
+
+ FlowGraph* callee_graph = call_data.callee_graph;
+ call_data.exit_collector->PrepareGraphs(callee_graph);
+ inlined_entries_.Add(callee_graph->graph_entry());
+ exit_collector_->Union(call_data.exit_collector);
+
+ // Replace parameter stubs and constants. Replace the receiver argument
+ // with a redefinition to prevent code from the inlined body from being
+ // hoisted above the inlined entry.
+ ASSERT(arguments.length() > 0);
+ Value* actual = arguments[0];
+ RedefinitionInstr* redefinition = new RedefinitionInstr(actual->Copy());
+ redefinition->set_ssa_temp_index(
+ owner_->caller_graph()->alloc_ssa_temp_index());
+ redefinition->InsertAfter(callee_graph->graph_entry()->normal_entry());
+ Definition* stub = (*call_data.parameter_stubs)[0];
+ stub->ReplaceUsesWith(redefinition);
+
+ for (intptr_t i = 1; i < arguments.length(); ++i) {
+ actual = arguments[i];
+ if (actual != NULL) {
+ stub = (*call_data.parameter_stubs)[i];
+ stub->ReplaceUsesWith(actual->definition());
+ }
+ }
+ GrowableArray<Definition*>* defns =
+ callee_graph->graph_entry()->initial_definitions();
+ for (intptr_t i = 0; i < defns->length(); ++i) {
+ ConstantInstr* constant = (*defns)[i]->AsConstant();
+ if ((constant != NULL) && constant->HasUses()) {
+ constant->ReplaceUsesWith(
+ owner_->caller_graph()->GetConstant(constant->value()));
+ }
+ }
+ return true;
+}
+
+
+static Instruction* AppendInstruction(Instruction* first,
+ Instruction* second) {
+ for (intptr_t i = second->InputCount() - 1; i >= 0; --i) {
+ Value* input = second->InputAt(i);
+ input->definition()->AddInputUse(input);
+ }
+ first->LinkTo(second);
+ return second;
+}
+
+
+// Build a DAG to dispatch to the inlined function bodies. Load the class
+// id of the receiver and make explicit comparisons for each inlined body,
+// in frequency order. If all variants are inlined, the entry to the last
+// inlined body is guarded by a CheckClassId instruction which can deopt.
+// If not all variants are inlined, we add a PolymorphicInstanceCall
+// instruction to handle the non-inlined variants.
+TargetEntryInstr* PolymorphicInliner::BuildDecisionGraph() {
+ // Start with a fresh target entry.
+ TargetEntryInstr* entry =
+ new TargetEntryInstr(owner_->caller_graph()->allocate_block_id(),
+ call_->GetBlock()->try_index());
+ entry->InheritDeoptTarget(call_);
+
+ // This function uses a cursor (a pointer to the 'current' instruction) to
+ // build the graph. The next instruction will be inserted after the
+ // cursor.
+ TargetEntryInstr* current_block = entry;
+ Instruction* cursor = entry;
+
+ Definition* receiver = call_->ArgumentAt(0);
+ // There are at least two variants including non-inlined ones, so we have
+ // at least one branch on the class id.
+ LoadClassIdInstr* load_cid = new LoadClassIdInstr(new Value(receiver));
+ load_cid->set_ssa_temp_index(owner_->caller_graph()->alloc_ssa_temp_index());
+ cursor = AppendInstruction(cursor, load_cid);
+ for (intptr_t i = 0; i < inlined_variants_.length(); ++i) {
+ // 1. Guard the body with a class id check.
+ if ((i == (inlined_variants_.length() - 1)) &&
+ non_inlined_variants_.is_empty()) {
+ // If it is the last variant use a check class or check smi
+ // instruction which can deoptimize, followed unconditionally by the
+ // body.
+ if (inlined_variants_[i].cid == kSmiCid) {
+ CheckSmiInstr* check_smi =
+ new CheckSmiInstr(new Value(receiver), call_->deopt_id());
+ check_smi->InheritDeoptTarget(call_);
+ cursor = AppendInstruction(cursor, check_smi);
+ } else {
+ const ICData& old_checks = call_->ic_data();
+ const ICData& new_checks = ICData::ZoneHandle(
+ ICData::New(Function::Handle(old_checks.function()),
+ String::Handle(old_checks.target_name()),
+ old_checks.deopt_id(),
+ 1)); // Number of args tested.
+ new_checks.AddReceiverCheck(inlined_variants_[i].cid,
+ *inlined_variants_[i].target);
+ CheckClassInstr* check_class =
+ new CheckClassInstr(new Value(receiver),
+ call_->deopt_id(),
+ new_checks);
+ check_class->InheritDeoptTarget(call_);
+ cursor = AppendInstruction(cursor, check_class);
+ }
+ // The next instruction is the first instruction of the inlined body.
+ // Handle the two possible cases (unshared and shared subsequent
+ // predecessors) separately.
+ BlockEntryInstr* callee_entry = inlined_entries_[i];
+ if (callee_entry->IsGraphEntry()) {
+ // Unshared. Graft the normal entry on after the check class
+ // instruction.
+ TargetEntryInstr* target =
+ callee_entry->AsGraphEntry()->normal_entry();
+ cursor->LinkTo(target->next());
+ target->ReplaceAsPredecessorWith(current_block);
+ // All blocks that were dominated by the normal entry are now
+ // dominated by the current block.
+ for (intptr_t j = 0;
+ j < target->dominated_blocks().length();
+ ++j) {
+ BlockEntryInstr* block = target->dominated_blocks()[j];
+ current_block->AddDominatedBlock(block);
+ }
+ } else if (callee_entry->IsJoinEntry()) {
+ // Shared inlined body and this is a subsequent entry. We have
+ // already constructed a join and set its dominator. Add a jump to
+ // the join.
+ JoinEntryInstr* join = callee_entry->AsJoinEntry();
+ ASSERT(join->dominator() != NULL);
+ GotoInstr* goto_join = new GotoInstr(join);
+ goto_join->InheritDeoptTarget(join);
+ cursor->LinkTo(goto_join);
+ current_block->set_last_instruction(goto_join);
+ } else {
+ // There is no possibility of a TargetEntry (the first entry to a
+ // shared inlined body) because this is the last inlined entry.
+ UNREACHABLE();
+ }
+ cursor = NULL;
+ } else {
+ // For all variants except the last, use a branch on the loaded class
+ // id.
+ const Smi& cid = Smi::ZoneHandle(Smi::New(inlined_variants_[i].cid));
+ ConstantInstr* cid_constant = new ConstantInstr(cid);
+ cid_constant->set_ssa_temp_index(
+ owner_->caller_graph()->alloc_ssa_temp_index());
+ StrictCompareInstr* compare =
+ new StrictCompareInstr(Token::kEQ_STRICT,
+ new Value(load_cid),
+ new Value(cid_constant));
+ BranchInstr* branch = new BranchInstr(compare);
+ branch->InheritDeoptTarget(call_);
+ AppendInstruction(AppendInstruction(cursor, cid_constant), branch);
+ current_block->set_last_instruction(branch);
+ cursor = NULL;
+
+ // 2. Handle a match by linking to the inlined body. There are three
+ // cases (unshared, shared first predecessor, and shared subsequent
+ // predecessors).
+ BlockEntryInstr* callee_entry = inlined_entries_[i];
+ TargetEntryInstr* true_target = NULL;
+ if (callee_entry->IsGraphEntry()) {
+ // Unshared.
+ true_target = callee_entry->AsGraphEntry()->normal_entry();
+ } else if (callee_entry->IsTargetEntry()) {
+ // Shared inlined body and this is the first entry. We have already
+ // constructed a join and this target jumps to it.
+ true_target = callee_entry->AsTargetEntry();
+ BlockEntryInstr* join =
+ true_target->last_instruction()->SuccessorAt(0);
+ current_block->AddDominatedBlock(join);
+ } else {
+ // Shared inlined body and this is a subsequent entry. We have
+ // already constructed a join. We need a fresh target that jumps to
+ // the join.
+ JoinEntryInstr* join = callee_entry->AsJoinEntry();
+ ASSERT(join != NULL);
+ ASSERT(join->dominator() != NULL);
+ true_target =
+ new TargetEntryInstr(owner_->caller_graph()->allocate_block_id(),
+ call_->GetBlock()->try_index());
+ true_target->InheritDeoptTarget(join);
+ GotoInstr* goto_join = new GotoInstr(join);
+ goto_join->InheritDeoptTarget(join);
+ true_target->LinkTo(goto_join);
+ true_target->set_last_instruction(goto_join);
+ }
+ *branch->true_successor_address() = true_target;
+ current_block->AddDominatedBlock(true_target);
+
+ // 3. Prepare to handle a match failure on the next iteration or the
+ // fall-through code below for non-inlined variants.
+ TargetEntryInstr* false_target =
+ new TargetEntryInstr(owner_->caller_graph()->allocate_block_id(),
+ call_->GetBlock()->try_index());
+ false_target->InheritDeoptTarget(call_);
+ *branch->false_successor_address() = false_target;
+ current_block->AddDominatedBlock(false_target);
+ cursor = current_block = false_target;
+ }
+ }
+
+ // Handle any non-inlined variants.
+ if (!non_inlined_variants_.is_empty()) {
+ // Move push arguments of the call.
+ for (intptr_t i = 0; i < call_->ArgumentCount(); ++i) {
+ PushArgumentInstr* push = call_->PushArgumentAt(i);
+ push->ReplaceUsesWith(push->value()->definition());
+ push->previous()->LinkTo(push->next());
+ cursor->LinkTo(push);
+ cursor = push;
+ }
+ const ICData& old_checks = call_->ic_data();
+ const ICData& new_checks = ICData::ZoneHandle(
+ ICData::New(Function::Handle(old_checks.function()),
+ String::Handle(old_checks.target_name()),
+ old_checks.deopt_id(),
+ 1)); // Number of args tested.
+ for (intptr_t i = 0; i < non_inlined_variants_.length(); ++i) {
+ new_checks.AddReceiverCheck(non_inlined_variants_[i].cid,
+ *non_inlined_variants_[i].target,
+ non_inlined_variants_[i].count);
+ }
+ PolymorphicInstanceCallInstr* fallback_call =
+ new PolymorphicInstanceCallInstr(call_->instance_call(),
+ new_checks,
+ true); // With checks.
+ fallback_call->set_ssa_temp_index(
+ owner_->caller_graph()->alloc_ssa_temp_index());
+ fallback_call->InheritDeoptTarget(call_);
+ ReturnInstr* fallback_return =
+ new ReturnInstr(call_->instance_call()->token_pos(),
+ new Value(fallback_call));
+ fallback_return->InheritDeoptTargetAfter(call_);
+ AppendInstruction(AppendInstruction(cursor, fallback_call),
+ fallback_return);
+ exit_collector_->AddExit(fallback_return);
+ cursor = NULL;
+ } else {
+ // Remove push arguments of the call.
+ for (intptr_t i = 0; i < call_->ArgumentCount(); ++i) {
+ PushArgumentInstr* push = call_->PushArgumentAt(i);
+ push->ReplaceUsesWith(push->value()->definition());
+ push->RemoveFromGraph();
+ }
+ }
+ return entry;
+}
+
+
+void PolymorphicInliner::Inline() {
+ // Consider the polymorphic variants in order by frequency.
+ FlowGraphCompiler::SortICDataByCount(call_->ic_data(), &variants_);
+ for (intptr_t var_idx = 0; var_idx < variants_.length(); ++var_idx) {
+ const Function& target = *variants_[var_idx].target;
+
+ // First check if this is the same target as an earlier inlined variant.
+ if (CheckInlinedDuplicate(target)) {
+ inlined_variants_.Add(variants_[var_idx]);
+ continue;
+ }
+
+ // Also check if this is the same target as an earlier non-inlined
+ // variant. If so and since inlining decisions are costly, do not try
+ // to inline this variant.
+ if (CheckNonInlinedDuplicate(target)) {
+ non_inlined_variants_.Add(variants_[var_idx]);
+ continue;
+ }
+
+ // Make an inlining decision.
+ if (TryInlining(target)) {
+ inlined_variants_.Add(variants_[var_idx]);
+ } else {
+ non_inlined_variants_.Add(variants_[var_idx]);
+ }
+ }
+
+ // If there are no inlined variants, leave the call in place.
+ if (inlined_variants_.is_empty()) return;
+
+ // Now build a decision tree (a DAG because of shared inline variants) and
+ // inline it at the call site.
+ TargetEntryInstr* entry = BuildDecisionGraph();
+ exit_collector_->ReplaceCall(entry);
+}
+
+
void FlowGraphInliner::CollectGraphInfo(FlowGraph* flow_graph) {
GraphInfoCollector info;
info.Collect(*flow_graph);
diff --git a/runtime/vm/flow_graph_optimizer.cc b/runtime/vm/flow_graph_optimizer.cc
index 3182806..3db0a65 100644
--- a/runtime/vm/flow_graph_optimizer.cc
+++ b/runtime/vm/flow_graph_optimizer.cc
@@ -306,10 +306,9 @@
bool changed = false;
for (intptr_t i = 0; i < block_order_.length(); ++i) {
BlockEntryInstr* entry = block_order_[i];
- entry->Accept(this);
for (ForwardInstructionIterator it(entry); !it.Done(); it.Advance()) {
Instruction* current = it.Current();
- Instruction* replacement = current->Canonicalize(this);
+ Instruction* replacement = current->Canonicalize(flow_graph());
if (replacement != current) {
// For non-definitions Canonicalize should return either NULL or
// this.
@@ -363,8 +362,7 @@
const double dbl_val = Smi::Cast(constant->value()).AsDoubleValue();
const Double& dbl_obj =
Double::ZoneHandle(Double::New(dbl_val, Heap::kOld));
- ConstantInstr* double_const = new ConstantInstr(dbl_obj);
- InsertBefore(insert_before, double_const, NULL, Definition::kValue);
+ ConstantInstr* double_const = flow_graph()->GetConstant(dbl_obj);
converted = new UnboxDoubleInstr(new Value(double_const), deopt_id);
} else {
converted = new UnboxDoubleInstr(use->CopyWithType(), deopt_id);
@@ -1131,8 +1129,7 @@
call->env(),
Definition::kEffect);
ConstantInstr* constant =
- new ConstantInstr(Smi::Handle(Smi::New(value - 1)));
- InsertBefore(call, constant, NULL, Definition::kValue);
+ flow_graph()->GetConstant(Smi::Handle(Smi::New(value - 1)));
BinarySmiOpInstr* bin_op =
new BinarySmiOpInstr(Token::kBIT_AND, call,
new Value(left),
@@ -1178,8 +1175,7 @@
(op_kind == Token::kNEGATE)) {
AddReceiverCheck(call);
ConstantInstr* minus_one =
- new ConstantInstr(Double::ZoneHandle(Double::NewCanonical(-1)));
- InsertBefore(call, minus_one, NULL, Definition::kValue);
+ flow_graph()->GetConstant(Double::ZoneHandle(Double::NewCanonical(-1)));
unary_op = new BinaryDoubleOpInstr(Token::kMUL,
new Value(input),
new Value(minus_one),
@@ -1372,9 +1368,7 @@
LoadFieldInstr* load = BuildLoadStringLength(call->ArgumentAt(0));
InsertBefore(call, load, NULL, Definition::kValue);
- ConstantInstr* zero = new ConstantInstr(Smi::Handle(Smi::New(0)));
- InsertBefore(call, zero, NULL, Definition::kValue);
-
+ ConstantInstr* zero = flow_graph()->GetConstant(Smi::Handle(Smi::New(0)));
StrictCompareInstr* compare =
new StrictCompareInstr(Token::kEQ_STRICT,
new Value(load),
@@ -2025,8 +2019,7 @@
// len_in_bytes = length * kBytesPerElement(receiver)
intptr_t element_size = FlowGraphCompiler::ElementSizeFor(receiver_cid);
ConstantInstr* bytes_per_element =
- new ConstantInstr(Smi::Handle(Smi::New(element_size)));
- InsertBefore(call, bytes_per_element, NULL, Definition::kValue);
+ flow_graph()->GetConstant(Smi::Handle(Smi::New(element_size)));
BinarySmiOpInstr* len_in_bytes =
new BinarySmiOpInstr(Token::kMUL,
call,
@@ -2113,8 +2106,15 @@
if (negate) {
as_bool = Bool::Get(!as_bool.value());
}
- ConstantInstr* bool_const = new ConstantInstr(as_bool);
- ReplaceCall(call, bool_const);
+ ConstantInstr* bool_const = flow_graph()->GetConstant(as_bool);
+ for (intptr_t i = 0; i < call->ArgumentCount(); ++i) {
+ PushArgumentInstr* push = call->PushArgumentAt(i);
+ push->ReplaceUsesWith(push->value()->definition());
+ push->RemoveFromGraph();
+ }
+ call->ReplaceUsesWith(bool_const);
+ ASSERT(current_iterator()->Current() == call);
+ current_iterator()->RemoveCurrentFromGraph();
return;
}
}
@@ -2679,6 +2679,15 @@
void RangeAnalysis::CollectSmiValues() {
+ const GrowableArray<Definition*>& initial =
+ *flow_graph_->graph_entry()->initial_definitions();
+ for (intptr_t i = 0; i < initial.length(); ++i) {
+ Definition* current = initial[i];
+ if (current->Type()->ToCid() == kSmiCid) {
+ smi_values_.Add(current);
+ }
+ }
+
for (BlockIterator block_it = flow_graph_->reverse_postorder_iterator();
!block_it.Done();
block_it.Advance()) {
@@ -2702,7 +2711,7 @@
if (join != NULL) {
for (PhiIterator phi_it(join); !phi_it.Done(); phi_it.Advance()) {
PhiInstr* current = phi_it.Current();
- if ((current->Type()->ToCid() == kSmiCid)) {
+ if (current->Type()->ToCid() == kSmiCid) {
smi_values_.Add(current);
}
}
@@ -3106,6 +3115,14 @@
}
// Infer initial values of ranges.
+ const GrowableArray<Definition*>& initial =
+ *flow_graph_->graph_entry()->initial_definitions();
+ for (intptr_t i = 0; i < initial.length(); ++i) {
+ Definition* definition = initial[i];
+ if (smi_definitions_->Contains(definition->ssa_temp_index())) {
+ definition->InferRange();
+ }
+ }
InferRangesRecursive(flow_graph_->graph_entry());
if (FLAG_trace_range_analysis) {
@@ -3148,6 +3165,7 @@
LICM::LICM(FlowGraph* flow_graph) : flow_graph_(flow_graph) {
+ ASSERT(flow_graph->is_licm_allowed());
}
@@ -3238,8 +3256,7 @@
!it.Done();
it.Advance()) {
Instruction* current = it.Current();
- if (!current->IsPushArgument() &&
- current->AllowsCSE() &&
+ if (current->AllowsCSE() &&
flow_graph()->block_effects()->CanBeMovedTo(current, pre_header)) {
bool inputs_loop_invariant = true;
for (int i = 0; i < current->InputCount(); ++i) {
@@ -3268,10 +3285,7 @@
static bool IsLoadEliminationCandidate(Definition* def) {
- // Immutable loads (not affected by side effects) are handled
- // in the DominatorBasedCSE pass.
- // TODO(fschneider): Extend to other load instructions.
- return (def->IsLoadField() && !def->Dependencies().IsNone())
+ return def->IsLoadField()
|| def->IsLoadIndexed()
|| def->IsLoadStaticField()
|| def->IsCurrentContext();
@@ -3344,14 +3358,19 @@
};
-// Set mapping alias to a list of loads sharing this alias.
+// Set mapping alias to a list of loads sharing this alias. Additionally
+// carries a set of loads that can be aliased by side-effects, essentially
+// those that are affected by calls.
class AliasedSet : public ZoneAllocated {
public:
explicit AliasedSet(intptr_t max_expr_id)
: max_expr_id_(max_expr_id),
sets_(),
- field_ids_(),
- max_field_id_(0) { }
+ // BitVector constructor throws if requested length is 0.
+ aliased_by_effects_(max_expr_id > 0 ? new BitVector(max_expr_id)
+ : NULL),
+ max_field_id_(0),
+ field_ids_() { }
Alias ComputeAliasForLoad(Definition* defn) {
if (defn->IsLoadIndexed()) {
@@ -3422,7 +3441,14 @@
return sets_[alias.ToIndex()];
}
- void Add(const Alias alias, intptr_t ssa_index) {
+ void AddRepresentative(Definition* defn) {
+ AddIdForAlias(ComputeAliasForLoad(defn), defn->expr_id());
+ if (!IsIndependentFromEffects(defn)) {
+ aliased_by_effects_->Add(defn->expr_id());
+ }
+ }
+
+ void AddIdForAlias(const Alias alias, intptr_t expr_id) {
const intptr_t idx = alias.ToIndex();
while (sets_.length() <= idx) {
@@ -3433,19 +3459,15 @@
sets_[idx] = new BitVector(max_expr_id_);
}
- sets_[idx]->Add(ssa_index);
+ sets_[idx]->Add(expr_id);
}
intptr_t max_expr_id() const { return max_expr_id_; }
bool IsEmpty() const { return max_expr_id_ == 0; }
+ BitVector* aliased_by_effects() const { return aliased_by_effects_; }
+
private:
- const intptr_t max_expr_id_;
-
- // Maps alias index to a set of ssa indexes corresponding to loads with the
- // given alias.
- GrowableArray<BitVector*> sets_;
-
// Get id assigned to the given field. Assign a new id if the field is seen
// for the first time.
intptr_t GetFieldId(intptr_t instance_id, const Field& field) {
@@ -3509,15 +3531,51 @@
escapes = true;
break;
}
-
- alloc->set_identity(escapes ? AllocateObjectInstr::kAliased
- : AllocateObjectInstr::kNotAliased);
}
+
+ alloc->set_identity(escapes ? AllocateObjectInstr::kAliased
+ : AllocateObjectInstr::kNotAliased);
}
return alloc->identity() != AllocateObjectInstr::kNotAliased;
}
+ // Returns true if the given load is unaffected by external side-effects.
+ // This essentially means that no stores to the same location can
+ // occur in other functions.
+ bool IsIndependentFromEffects(Definition* defn) {
+ LoadFieldInstr* load_field = defn->AsLoadField();
+ if (load_field != NULL) {
+ // Note that we can't use LoadField's is_immutable attribute here because
+ // some VM-fields (those that have no corresponding Field object and
+ // accessed through offset alone) can share offset but have different
+ // immutability properties.
+ // One example is the length property of growable and fixed size list. If
+ // loads of these two properties occur in the same function for the same
+ // receiver then they will get the same expression number. However
+ // immutability of the length of fixed size list does not mean that
+ // growable list also has immutable property. Thus we will make a
+ // conservative assumption for the VM-properties.
+ // TODO(vegorov): disambiguate immutable and non-immutable VM-fields with
+ // the same offset e.g. through recognized kind.
+ if ((load_field->field() != NULL) &&
+ (load_field->field()->is_final())) {
+ return true;
+ }
+
+ AllocateObjectInstr* alloc =
+ load_field->instance()->definition()->AsAllocateObject();
+ return (alloc != NULL) && !CanBeAliased(alloc);
+ }
+
+ LoadStaticFieldInstr* load_static_field = defn->AsLoadStaticField();
+ if (load_static_field != NULL) {
+ return load_static_field->field().is_final();
+ }
+
+ return false;
+ }
+
class FieldIdPair {
public:
struct Key {
@@ -3555,9 +3613,17 @@
Value value_;
};
+ const intptr_t max_expr_id_;
+
+ // Maps alias index to a set of ssa indexes corresponding to loads with the
+ // given alias.
+ GrowableArray<BitVector*> sets_;
+
+ BitVector* aliased_by_effects_;
+
// Table mapping static field to their id used during optimization pass.
- DirectChainedHashMap<FieldIdPair> field_ids_;
intptr_t max_field_id_;
+ DirectChainedHashMap<FieldIdPair> field_ids_;
};
@@ -3724,7 +3790,7 @@
AliasedSet* aliased_set = new AliasedSet(expr_id);
for (intptr_t i = 0; i < loads.length(); i++) {
Definition* defn = loads[i];
- aliased_set->Add(aliased_set->ComputeAliasForLoad(defn), defn->expr_id());
+ aliased_set->AddRepresentative(defn);
}
return aliased_set;
}
@@ -3760,6 +3826,25 @@
}
}
+ static bool OptimizeGraph(FlowGraph* graph) {
+ ASSERT(FLAG_load_cse);
+
+ DirectChainedHashMap<LoadKeyValueTrait> map;
+ AliasedSet* aliased_set = NumberLoadExpressions(graph, &map);
+ if (!aliased_set->IsEmpty()) {
+ // If any loads were forwarded return true from Optimize to run load
+ // forwarding again. This will allow to forward chains of loads.
+ // This is especially important for context variables as they are built
+ // as loads from loaded context.
+ // TODO(vegorov): renumber newly discovered congruences during the
+ // forwarding to forward chains without running whole pass twice.
+ LoadOptimizer load_optimizer(graph, aliased_set, &map);
+ return load_optimizer.Optimize();
+ }
+ return false;
+ }
+
+ private:
bool Optimize() {
ComputeInitialSets();
ComputeOutValues();
@@ -3768,7 +3853,6 @@
return forwarded_;
}
- private:
// Compute sets of loads generated and killed by each block.
// Additionally compute upwards exposed and generated loads for each block.
// Exposed loads are those that can be replaced if a corresponding
@@ -3826,18 +3910,59 @@
continue;
}
- // Other instructions with side effects kill all loads.
+ // If instruction has effects then kill all loads affected.
if (!instr->Effects().IsNone()) {
- kill->SetAll();
- // There is no need to clear out_values when clearing GEN set
- // because only those values that are in the GEN set
+ kill->AddAll(aliased_set_->aliased_by_effects());
+ // There is no need to clear out_values when removing values from GEN
+ // set because only those values that are in the GEN set
// will ever be used.
- gen->Clear();
+ gen->RemoveAll(aliased_set_->aliased_by_effects());
continue;
}
Definition* defn = instr->AsDefinition();
- if ((defn == NULL) || !IsLoadEliminationCandidate(defn)) {
+ if (defn == NULL) {
+ continue;
+ }
+
+ // For object allocation forward initial values of the fields to
+ // subsequent loads.
+ // For simplicity we ignore escaping objects and objects that have
+ // type arguments.
+ // The reason to ignore escaping objects is that final fields are
+ // initialized in constructor that potentially can be not inlined into
+ // the function that we are currently optimizing. However at the same
+ // time we assume that values of the final fields can be forwarded
+ // across side-effects. If we add 'null' as known values for these
+ // fields here we will incorrectly propagate this null across
+ // constructor invocation.
+ // TODO(vegorov): record null-values at least for not final fields of
+ // escaping object.
+ // TODO(vegorov): enable forwarding of type arguments.
+ AllocateObjectInstr* alloc = instr->AsAllocateObject();
+ if ((alloc != NULL) &&
+ (alloc->identity() == AllocateObjectInstr::kNotAliased) &&
+ (alloc->ArgumentCount() == 0)) {
+ for (Value* use = alloc->input_use_list();
+ use != NULL;
+ use = use->next_use()) {
+ // Look for all immediate loads from this object.
+ if (use->use_index() != 0) {
+ continue;
+ }
+
+ LoadFieldInstr* load = use->instruction()->AsLoadField();
+ if (load != NULL) {
+ // Found a load. Initialize current value of the field to null.
+ gen->Add(load->expr_id());
+ if (out_values == NULL) out_values = CreateBlockOutValues();
+ (*out_values)[load->expr_id()] = graph_->constant_null();
+ }
+ }
+ continue;
+ }
+
+ if (!IsLoadEliminationCandidate(defn)) {
continue;
}
@@ -4241,19 +4366,7 @@
bool DominatorBasedCSE::Optimize(FlowGraph* graph) {
bool changed = false;
if (FLAG_load_cse) {
- GrowableArray<BitVector*> kill_by_offs(10);
- DirectChainedHashMap<LoadKeyValueTrait> map;
- AliasedSet* aliased_set = NumberLoadExpressions(graph, &map);
- if (!aliased_set->IsEmpty()) {
- // If any loads were forwarded return true from Optimize to run load
- // forwarding again. This will allow to forward chains of loads.
- // This is especially important for context variables as they are built
- // as loads from loaded context.
- // TODO(vegorov): renumber newly discovered congruences during the
- // forwarding to forward chains without running whole pass twice.
- LoadOptimizer load_optimizer(graph, aliased_set, &map);
- changed = load_optimizer.Optimize() || changed;
- }
+ changed = LoadOptimizer::OptimizeGraph(graph) || changed;
}
CSEInstructionMap map;
@@ -4530,6 +4643,11 @@
}
+void ConstantPropagator::VisitRedefinition(RedefinitionInstr* instr) {
+ SetValue(instr, instr->value()->definition()->constant_value());
+}
+
+
void ConstantPropagator::VisitParameter(ParameterInstr* instr) {
SetValue(instr, non_constant_);
}
@@ -4804,6 +4922,11 @@
}
+void ConstantPropagator::VisitLoadClassId(LoadClassIdInstr* instr) {
+ SetValue(instr, non_constant_);
+}
+
+
void ConstantPropagator::VisitLoadField(LoadFieldInstr* instr) {
if ((instr->recognized_kind() == MethodRecognizer::kObjectArrayLength) &&
(instr->instance()->definition()->IsCreateArray())) {
@@ -5023,6 +5146,12 @@
}
+void ConstantPropagator::VisitMaterializeObject(MaterializeObjectInstr* instr) {
+ // Should not be used outside of allocation elimination pass.
+ UNREACHABLE();
+}
+
+
void ConstantPropagator::VisitBinaryDoubleOp(
BinaryDoubleOpInstr* instr) {
const Object& left = instr->left()->definition()->constant_value();
@@ -5350,7 +5479,9 @@
defn->ssa_temp_index(),
defn->constant_value().ToCString());
}
- defn->ReplaceWith(new ConstantInstr(defn->constant_value()), &i);
+ ConstantInstr* constant = graph_->GetConstant(defn->constant_value());
+ defn->ReplaceUsesWith(constant);
+ i.RemoveCurrentFromGraph();
}
}
@@ -5440,10 +5571,9 @@
// Branch(Comparison(kind, Phi, Constant))
//
// These are the branches produced by inlining in a test context. Also,
- // the phi and the constant have no other uses so they can simply be
- // eliminated. The block has no other phis and no instructions
- // intervening between the phi, constant, and branch so the block can
- // simply be eliminated.
+ // the phi has no other uses so they can simply be eliminated. The block
+ // has no other phis and no instructions intervening between the phi and
+ // branch so the block can simply be eliminated.
BranchInstr* branch = block->last_instruction()->AsBranch();
ASSERT(branch != NULL);
ComparisonInstr* comparison = branch->comparison();
@@ -5455,9 +5585,7 @@
(constant != NULL) &&
(phi->GetBlock() == block) &&
PhiHasSingleUse(phi, left) &&
- constant->HasOnlyUse(right) &&
- (block->next() == constant) &&
- (constant->next() == branch) &&
+ (block->next() == branch) &&
(block->phis()->length() == 1);
}
@@ -5476,14 +5604,6 @@
}
-ConstantInstr* BranchSimplifier::CloneConstant(FlowGraph* flow_graph,
- ConstantInstr* constant) {
- ConstantInstr* new_constant = new ConstantInstr(constant->value());
- new_constant->set_ssa_temp_index(flow_graph->alloc_ssa_temp_index());
- return new_constant;
-}
-
-
BranchInstr* BranchSimplifier::CloneBranch(BranchInstr* branch,
Value* left,
Value* right) {
@@ -5569,16 +5689,35 @@
block->PredecessorAt(i)->last_instruction()->AsGoto();
ASSERT(old_goto != NULL);
- // Insert a copy of the constant in all the predecessors.
- ConstantInstr* new_constant = CloneConstant(flow_graph, constant);
- new_constant->InsertBefore(old_goto);
-
// Replace the goto in each predecessor with a rewritten branch,
// rewritten to use the corresponding phi input instead of the phi.
Value* new_left = phi->InputAt(i)->Copy();
- Value* new_right = new Value(new_constant);
+ Value* new_right = new Value(constant);
BranchInstr* new_branch = CloneBranch(branch, new_left, new_right);
- new_branch->InheritDeoptTarget(old_goto);
+ if (branch->env() == NULL) {
+ new_branch->InheritDeoptTarget(old_goto);
+ } else {
+ // Take the environment from the branch if it has one.
+ new_branch->InheritDeoptTarget(branch);
+ // InheritDeoptTarget gave the new branch's comparison the same
+ // deopt id that it gave the new branch. The id should be the
+ // deopt id of the original comparison.
+ new_branch->comparison()->SetDeoptId(comparison->GetDeoptId());
+ // The phi can be used in the branch's environment. Rename such
+ // uses.
+ for (Environment::DeepIterator it(new_branch->env());
+ !it.Done();
+ it.Advance()) {
+ Value* use = it.CurrentValue();
+ if (use->definition() == phi) {
+ Definition* replacement = phi->InputAt(i)->definition();
+ use->RemoveFromUseList();
+ use->set_definition(replacement);
+ replacement->AddEnvUse(use);
+ }
+ }
+ }
+
new_branch->InsertBefore(old_goto);
new_branch->set_next(NULL); // Detaching the goto from the graph.
old_goto->UnuseAllInputs();
@@ -5616,6 +5755,7 @@
phi->UnuseAllInputs();
branch->UnuseAllInputs();
block->UnuseAllInputs();
+ ASSERT(!phi->HasUses());
}
}
@@ -5757,4 +5897,240 @@
}
+void FlowGraphOptimizer::EliminateEnvironments() {
+ // After this pass we can no longer perform LICM and hoist instructions
+ // that can deoptimize.
+
+ flow_graph_->disallow_licm();
+ for (intptr_t i = 0; i < block_order_.length(); ++i) {
+ BlockEntryInstr* block = block_order_[i];
+ block->RemoveEnvironment();
+ for (ForwardInstructionIterator it(block); !it.Done(); it.Advance()) {
+ Instruction* current = it.Current();
+ if (!current->CanDeoptimize()) current->RemoveEnvironment();
+ }
+ }
+}
+
+
+// Right now we are attempting to sink allocation only into
+// deoptimization exit. So candidate should only be used in StoreInstanceField
+// instructions that write into fields of the allocated object.
+// We do not support materialization of the object that has type arguments.
+static bool IsAllocationSinkingCandidate(AllocateObjectInstr* alloc) {
+ // TODO(vegorov): support AllocateObject with type arguments.
+ if (alloc->ArgumentCount() > 0) {
+ return false;
+ }
+
+ for (Value* use = alloc->input_use_list();
+ use != NULL;
+ use = use->next_use()) {
+ if (!(use->instruction()->IsStoreInstanceField() &&
+ use->use_index() == 0)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+// Remove the given allocation from the graph. It is not observable.
+// If deoptimization occurs the object will be materialized.
+static void EliminateAllocation(AllocateObjectInstr* alloc) {
+ ASSERT(IsAllocationSinkingCandidate(alloc));
+
+ if (FLAG_trace_optimization) {
+ OS::Print("removing allocation from the graph: v%"Pd"\n",
+ alloc->ssa_temp_index());
+ }
+
+ // As an allocation sinking candidate it is only used in stores to its own
+ // fields. Remove these stores.
+ for (Value* use = alloc->input_use_list();
+ use != NULL;
+ use = alloc->input_use_list()) {
+ use->instruction()->RemoveFromGraph();
+ }
+
+ // There should be no environment uses. The pass replaced them with
+ // MaterializeObject instructions.
+ ASSERT(alloc->env_use_list() == NULL);
+ ASSERT(alloc->input_use_list() == NULL);
+ alloc->RemoveFromGraph();
+}
+
+
+void AllocationSinking::Optimize() {
+ GrowableArray<AllocateObjectInstr*> candidates(5);
+
+ // Collect sinking candidates.
+ const GrowableArray<BlockEntryInstr*>& postorder = flow_graph_->postorder();
+ for (BlockIterator block_it(postorder);
+ !block_it.Done();
+ block_it.Advance()) {
+ BlockEntryInstr* block = block_it.Current();
+ for (ForwardInstructionIterator it(block); !it.Done(); it.Advance()) {
+ AllocateObjectInstr* alloc = it.Current()->AsAllocateObject();
+ if ((alloc != NULL) && IsAllocationSinkingCandidate(alloc)) {
+ if (FLAG_trace_optimization) {
+ OS::Print("discovered allocation sinking candidate: v%"Pd"\n",
+ alloc->ssa_temp_index());
+ }
+ candidates.Add(alloc);
+ }
+ }
+ }
+
+ // Insert MaterializeObject instructions that will describe the state of the
+ // object at all deoptimization points. Each inserted materialization looks
+ // like this (where v_0 is allocation that we are going to eliminate):
+ // v_1 <- LoadField(v_0, field_1)
+ // ...
+ // v_N <- LoadField(v_0, field_N)
+ // v_{N+1} <- MaterializeObject(field_1 = v_1, ..., field_N = v_{N})
+ for (intptr_t i = 0; i < candidates.length(); i++) {
+ InsertMaterializations(candidates[i]);
+ }
+
+ // Run load forwarding to eliminate LoadField instructions inserted above.
+ // All loads will be successfully eliminated because:
+ // a) they use fields (not offsets) and thus provide precise aliasing
+ // information
+ // b) candidate does not escape and thus its fields is not affected by
+ // external effects from calls.
+ LoadOptimizer::OptimizeGraph(flow_graph_);
+
+ // At this point we have computed the state of object at each deoptimization
+ // point and we can eliminate it. Loads inserted above were forwarded so there
+ // are no uses of the allocation just as in the begging of the pass.
+ for (intptr_t i = 0; i < candidates.length(); i++) {
+ EliminateAllocation(candidates[i]);
+ }
+
+ // Process materializations and unbox their arguments: materializations
+ // are part of the environment and can materialize boxes for double/mint/simd
+ // values when needed.
+ // TODO(vegorov): handle all box types here.
+ for (intptr_t i = 0; i < materializations_.length(); i++) {
+ MaterializeObjectInstr* mat = materializations_[i];
+ for (intptr_t j = 0; j < mat->InputCount(); j++) {
+ Definition* defn = mat->InputAt(j)->definition();
+ if (defn->IsBoxDouble()) {
+ mat->InputAt(j)->BindTo(defn->InputAt(0)->definition());
+ }
+ }
+ }
+}
+
+
+// Remove materializations from the graph. Register allocator will treat them
+// as part of the environment not as a real instruction.
+void AllocationSinking::DetachMaterializations() {
+ for (intptr_t i = 0; i < materializations_.length(); i++) {
+ ASSERT(materializations_[i]->input_use_list() == NULL);
+ materializations_[i]->previous()->LinkTo(materializations_[i]->next());
+ }
+}
+
+
+// Add the given field to the list of fields if it is not yet present there.
+static void AddField(ZoneGrowableArray<const Field*>* fields,
+ const Field& field) {
+ for (intptr_t i = 0; i < fields->length(); i++) {
+ if ((*fields)[i]->raw() == field.raw()) {
+ return;
+ }
+ }
+ fields->Add(&field);
+}
+
+
+// Add given instruction to the list of the instructions if it is not yet
+// present there.
+static void AddInstruction(GrowableArray<Instruction*>* exits,
+ Instruction* exit) {
+ for (intptr_t i = 0; i < exits->length(); i++) {
+ if ((*exits)[i] == exit) {
+ return;
+ }
+ }
+ exits->Add(exit);
+}
+
+
+// Insert MaterializeObject instruction for the given allocation before
+// the given instruction that can deoptimize.
+void AllocationSinking::CreateMaterializationAt(
+ Instruction* exit,
+ AllocateObjectInstr* alloc,
+ const Class& cls,
+ const ZoneGrowableArray<const Field*>& fields) {
+ ZoneGrowableArray<Value*>* values =
+ new ZoneGrowableArray<Value*>(fields.length());
+
+ // Insert load instruction for every field.
+ for (intptr_t i = 0; i < fields.length(); i++) {
+ const Field* field = fields[i];
+ LoadFieldInstr* load = new LoadFieldInstr(new Value(alloc),
+ field->Offset(),
+ AbstractType::ZoneHandle());
+ load->set_field(field);
+ flow_graph_->InsertBefore(
+ exit, load, NULL, Definition::kValue);
+ values->Add(new Value(load));
+ }
+
+ MaterializeObjectInstr* mat = new MaterializeObjectInstr(cls, fields, values);
+ flow_graph_->InsertBefore(exit, mat, NULL, Definition::kValue);
+
+ // Replace all mentions of this allocation with a newly inserted
+ // MaterializeObject instruction.
+ // We must preserve the identity: all mentions are replaced by the same
+ // materialization.
+ for (Environment::DeepIterator env_it(exit->env());
+ !env_it.Done();
+ env_it.Advance()) {
+ Value* use = env_it.CurrentValue();
+ if (use->definition() == alloc) {
+ use->RemoveFromUseList();
+ use->set_definition(mat);
+ mat->AddEnvUse(use);
+ }
+ }
+
+ // Record inserted materialization.
+ materializations_.Add(mat);
+}
+
+
+void AllocationSinking::InsertMaterializations(AllocateObjectInstr* alloc) {
+ // Collect all fields that are written for this instance.
+ ZoneGrowableArray<const Field*>* fields =
+ new ZoneGrowableArray<const Field*>(5);
+
+ for (Value* use = alloc->input_use_list();
+ use != NULL;
+ use = use->next_use()) {
+ ASSERT(use->instruction()->IsStoreInstanceField());
+ AddField(fields, use->instruction()->AsStoreInstanceField()->field());
+ }
+
+ // Collect all instructions that mention this object in the environment.
+ GrowableArray<Instruction*> exits(10);
+ for (Value* use = alloc->env_use_list();
+ use != NULL;
+ use = use->next_use()) {
+ AddInstruction(&exits, use->instruction());
+ }
+
+ // Insert materializations at environment uses.
+ const Class& cls = Class::Handle(alloc->constructor().Owner());
+ for (intptr_t i = 0; i < exits.length(); i++) {
+ CreateMaterializationAt(exits[i], alloc, cls, *fields);
+ }
+}
+
+
} // namespace dart
diff --git a/runtime/vm/flow_graph_optimizer.h b/runtime/vm/flow_graph_optimizer.h
index 5b2bfa6..46111e9 100644
--- a/runtime/vm/flow_graph_optimizer.h
+++ b/runtime/vm/flow_graph_optimizer.h
@@ -44,6 +44,9 @@
void InferSmiRanges();
+ // Remove environments from the instructions which do not deoptimize.
+ void EliminateEnvironments();
+
virtual void VisitStaticCall(StaticCallInstr* instr);
virtual void VisitInstanceCall(InstanceCallInstr* instr);
virtual void VisitRelationalOp(RelationalOpInstr* instr);
@@ -291,19 +294,15 @@
public:
static void Simplify(FlowGraph* flow_graph);
- private:
- // Match an instance of the pattern to rewrite. See the implementation
- // for the patterns that are handled by this pass.
- static bool Match(JoinEntryInstr* block);
-
// Replace a target entry instruction with a join entry instruction. Does
// not update the original target's predecessors to point to the new block
// and does not replace the target in already computed block order lists.
static JoinEntryInstr* ToJoinEntry(TargetEntryInstr* target);
- // Duplicate a constant, assigning it a new SSA name.
- static ConstantInstr* CloneConstant(FlowGraph* flow_graph,
- ConstantInstr* constant);
+ private:
+ // Match an instance of the pattern to rewrite. See the implementation
+ // for the patterns that are handled by this pass.
+ static bool Match(JoinEntryInstr* block);
// Duplicate a branch while replacing its comparison's left and right
// inputs.
@@ -322,6 +321,30 @@
};
+class AllocationSinking : public ZoneAllocated {
+ public:
+ explicit AllocationSinking(FlowGraph* flow_graph)
+ : flow_graph_(flow_graph),
+ materializations_(5) { }
+
+ void Optimize();
+
+ void DetachMaterializations();
+
+ private:
+ void InsertMaterializations(AllocateObjectInstr* alloc);
+
+ void CreateMaterializationAt(
+ Instruction* exit,
+ AllocateObjectInstr* alloc,
+ const Class& cls,
+ const ZoneGrowableArray<const Field*>& fields);
+
+ FlowGraph* flow_graph_;
+
+ GrowableArray<MaterializeObjectInstr*> materializations_;
+};
+
} // namespace dart
#endif // VM_FLOW_GRAPH_OPTIMIZER_H_
diff --git a/runtime/vm/flow_graph_type_propagator.cc b/runtime/vm/flow_graph_type_propagator.cc
index 2b5f1a4..9cc0858 100644
--- a/runtime/vm/flow_graph_type_propagator.cc
+++ b/runtime/vm/flow_graph_type_propagator.cc
@@ -645,6 +645,16 @@
}
+CompileType RedefinitionInstr::ComputeType() const {
+ return CompileType::None();
+}
+
+
+bool RedefinitionInstr::RecomputeType() {
+ return UpdateType(*value()->Type());
+}
+
+
CompileType IfThenElseInstr::ComputeType() const {
ASSERT(InputCount() == 2);
return CompileType::FromCid(kSmiCid);
@@ -879,6 +889,11 @@
}
+CompileType LoadClassIdInstr::ComputeType() const {
+ return CompileType::FromCid(kSmiCid);
+}
+
+
CompileType LoadFieldInstr::ComputeType() const {
// Type may be null if the field is a VM field, e.g. context parent.
// Keep it as null for debug purposes and do not return dynamic in production
diff --git a/runtime/vm/il_printer.cc b/runtime/vm/il_printer.cc
index 406c445..ebac00a 100644
--- a/runtime/vm/il_printer.cc
+++ b/runtime/vm/il_printer.cc
@@ -494,6 +494,16 @@
}
+void MaterializeObjectInstr::PrintOperandsTo(BufferFormatter* f) const {
+ f->Print("%s", String::Handle(cls_.Name()).ToCString());
+ for (intptr_t i = 0; i < InputCount(); i++) {
+ f->Print(", ");
+ f->Print("%s: ", String::Handle(fields_[i]->name()).ToCString());
+ InputAt(i)->PrintTo(f);
+ }
+}
+
+
void CreateArrayInstr::PrintOperandsTo(BufferFormatter* f) const {
for (int i = 0; i < ArgumentCount(); ++i) {
if (i != 0) f->Print(", ");
@@ -735,7 +745,7 @@
void GraphEntryInstr::PrintTo(BufferFormatter* f) const {
const GrowableArray<Definition*>& defns = initial_definitions_;
- f->Print("B%"Pd"[graph]", block_id());
+ f->Print("B%"Pd"[graph]:%"Pd, block_id(), GetDeoptId());
if (defns.length() > 0) {
f->Print(" {");
for (intptr_t i = 0; i < defns.length(); ++i) {
@@ -749,7 +759,7 @@
void JoinEntryInstr::PrintTo(BufferFormatter* f) const {
- f->Print("B%"Pd"[join] pred(", block_id());
+ f->Print("B%"Pd"[join]:%"Pd" pred(", block_id(), GetDeoptId());
for (intptr_t i = 0; i < predecessors_.length(); ++i) {
if (i > 0) f->Print(", ");
f->Print("B%"Pd, predecessors_[i]->block_id());
@@ -800,7 +810,7 @@
void TargetEntryInstr::PrintTo(BufferFormatter* f) const {
- f->Print("B%"Pd"[target]", block_id());
+ f->Print("B%"Pd"[target]:%"Pd, block_id(), GetDeoptId());
if (HasParallelMove()) {
f->Print(" ");
parallel_move()->PrintTo(f);
diff --git a/runtime/vm/intermediate_language.cc b/runtime/vm/intermediate_language.cc
index bb57209..daa9d96 100644
--- a/runtime/vm/intermediate_language.cc
+++ b/runtime/vm/intermediate_language.cc
@@ -157,9 +157,12 @@
bool LoadFieldInstr::AttributesEqual(Instruction* other) const {
LoadFieldInstr* other_load = other->AsLoadField();
ASSERT(other_load != NULL);
- ASSERT((offset_in_bytes() != other_load->offset_in_bytes()) ||
- ((immutable_ == other_load->immutable_)));
- return offset_in_bytes() == other_load->offset_in_bytes();
+ if (field() != NULL) {
+ return (other_load->field() != NULL) &&
+ (field()->raw() == other_load->field()->raw());
+ }
+ return (other_load->field() == NULL) &&
+ (offset_in_bytes() == other_load->offset_in_bytes());
}
@@ -499,12 +502,16 @@
}
+// True if the definition has a single input use and is used only in
+// environments at the same instruction as that input use.
bool Definition::HasOnlyUse(Value* use) const {
- return (input_use_list() == use) &&
- (use->next_use() == NULL) &&
- ((env_use_list() == NULL) ||
- ((env_use_list()->instruction() == use->instruction()) &&
- (env_use_list()->next_use() == NULL)));
+ if ((input_use_list() != use) || (use->next_use() != NULL)) return false;
+
+ Instruction* target = use->instruction();
+ for (Value::Iterator it(env_use_list()); !it.Done(); it.Advance()) {
+ if (it.Current()->instruction() != target) return false;
+ }
+ return true;
}
@@ -1049,7 +1056,7 @@
}
-Definition* BinaryDoubleOpInstr::Canonicalize(FlowGraphOptimizer* optimizer) {
+Definition* BinaryDoubleOpInstr::Canonicalize(FlowGraph* flow_graph) {
Definition* result = NULL;
result = CanonicalizeCommutativeArithmetic(op_kind(),
@@ -1072,7 +1079,7 @@
}
-Definition* BinarySmiOpInstr::Canonicalize(FlowGraphOptimizer* optimizer) {
+Definition* BinarySmiOpInstr::Canonicalize(FlowGraph* flow_graph) {
Definition* result = NULL;
result = CanonicalizeCommutativeArithmetic(op_kind(),
@@ -1095,7 +1102,7 @@
}
-Definition* BinaryMintOpInstr::Canonicalize(FlowGraphOptimizer* optimizer) {
+Definition* BinaryMintOpInstr::Canonicalize(FlowGraph* flow_graph) {
Definition* result = NULL;
result = CanonicalizeCommutativeArithmetic(op_kind(),
@@ -1119,12 +1126,12 @@
// Optimizations that eliminate or simplify individual instructions.
-Instruction* Instruction::Canonicalize(FlowGraphOptimizer* optimizer) {
+Instruction* Instruction::Canonicalize(FlowGraph* flow_graph) {
return this;
}
-Definition* Definition::Canonicalize(FlowGraphOptimizer* optimizer) {
+Definition* Definition::Canonicalize(FlowGraph* flow_graph) {
return this;
}
@@ -1163,21 +1170,14 @@
bool LoadFieldInstr::IsFixedLengthArrayCid(intptr_t cid) {
+ if (RawObject::IsTypedDataClassId(cid) ||
+ RawObject::IsExternalTypedDataClassId(cid)) {
+ return true;
+ }
+
switch (cid) {
case kArrayCid:
case kImmutableArrayCid:
- case kTypedDataInt8ArrayCid:
- case kTypedDataUint8ArrayCid:
- case kTypedDataUint8ClampedArrayCid:
- case kTypedDataInt16ArrayCid:
- case kTypedDataUint16ArrayCid:
- case kTypedDataInt32ArrayCid:
- case kTypedDataUint32ArrayCid:
- case kTypedDataInt64ArrayCid:
- case kTypedDataUint64ArrayCid:
- case kTypedDataFloat32ArrayCid:
- case kTypedDataFloat64ArrayCid:
- case kTypedDataFloat32x4ArrayCid:
return true;
default:
return false;
@@ -1185,12 +1185,12 @@
}
-Definition* ConstantInstr::Canonicalize(FlowGraphOptimizer* optimizer) {
+Definition* ConstantInstr::Canonicalize(FlowGraph* flow_graph) {
return HasUses() ? this : NULL;
}
-Definition* LoadFieldInstr::Canonicalize(FlowGraphOptimizer* optimizer) {
+Definition* LoadFieldInstr::Canonicalize(FlowGraph* flow_graph) {
if (!IsImmutableLengthLoad()) return this;
// For fixed length arrays if the array is the result of a known constructor
@@ -1206,7 +1206,7 @@
}
-Definition* AssertBooleanInstr::Canonicalize(FlowGraphOptimizer* optimizer) {
+Definition* AssertBooleanInstr::Canonicalize(FlowGraph* flow_graph) {
if (FLAG_eliminate_type_checks && (value()->Type()->ToCid() == kBoolCid)) {
return value()->definition();
}
@@ -1215,7 +1215,7 @@
}
-Definition* AssertAssignableInstr::Canonicalize(FlowGraphOptimizer* optimizer) {
+Definition* AssertAssignableInstr::Canonicalize(FlowGraph* flow_graph) {
if (FLAG_eliminate_type_checks &&
value()->Type()->IsAssignableTo(dst_type())) {
return value()->definition();
@@ -1235,17 +1235,14 @@
const AbstractType& new_dst_type = AbstractType::Handle(
dst_type().InstantiateFrom(instantiator_type_args, NULL));
set_dst_type(AbstractType::ZoneHandle(new_dst_type.Canonicalize()));
- ConstantInstr* null_constant = new ConstantInstr(Object::ZoneHandle());
- // It is ok to insert instructions before the current during
- // forward iteration.
- optimizer->InsertBefore(this, null_constant, NULL, Definition::kValue);
+ ConstantInstr* null_constant = flow_graph->constant_null();
instantiator_type_arguments()->BindTo(null_constant);
}
return this;
}
-Definition* BoxDoubleInstr::Canonicalize(FlowGraphOptimizer* optimizer) {
+Definition* BoxDoubleInstr::Canonicalize(FlowGraph* flow_graph) {
if (input_use_list() == NULL) {
// Environments can accomodate any representation. No need to box.
return value()->definition();
@@ -1261,17 +1258,17 @@
}
-Definition* UnboxDoubleInstr::Canonicalize(FlowGraphOptimizer* optimizer) {
+Definition* UnboxDoubleInstr::Canonicalize(FlowGraph* flow_graph) {
// Fold away UnboxDouble(BoxDouble(v)).
BoxDoubleInstr* defn = value()->definition()->AsBoxDouble();
return (defn != NULL) ? defn->value()->definition() : this;
}
-Instruction* BranchInstr::Canonicalize(FlowGraphOptimizer* optimizer) {
+Instruction* BranchInstr::Canonicalize(FlowGraph* flow_graph) {
// Only handle strict-compares.
if (comparison()->IsStrictCompare()) {
- Definition* replacement = comparison()->Canonicalize(optimizer);
+ Definition* replacement = comparison()->Canonicalize(flow_graph);
if ((replacement == comparison()) || (replacement == NULL)) {
return this;
}
@@ -1294,7 +1291,7 @@
Value* use = comp->input_use_list();
if ((use->instruction() == this) && comp->HasOnlyUse(use)) {
RemoveEnvironment();
- InheritDeoptTarget(comp);
+ flow_graph->CopyDeoptTarget(this, comp);
comp->RemoveFromGraph();
SetComparison(comp);
@@ -1312,7 +1309,7 @@
}
-Definition* StrictCompareInstr::Canonicalize(FlowGraphOptimizer* optimizer) {
+Definition* StrictCompareInstr::Canonicalize(FlowGraph* flow_graph) {
if (!right()->BindsToConstant()) {
return this;
}
@@ -1342,7 +1339,7 @@
}
-Instruction* CheckClassInstr::Canonicalize(FlowGraphOptimizer* optimizer) {
+Instruction* CheckClassInstr::Canonicalize(FlowGraph* flow_graph) {
// TODO(vegorov): Replace class checks with null checks when ToNullableCid
// matches.
@@ -1355,7 +1352,7 @@
}
-Instruction* GuardFieldInstr::Canonicalize(FlowGraphOptimizer* optimizer) {
+Instruction* GuardFieldInstr::Canonicalize(FlowGraph* flow_graph) {
if (field().guarded_cid() == kDynamicCid) {
return NULL; // Nothing to guard.
}
@@ -1374,13 +1371,12 @@
}
-Instruction* CheckSmiInstr::Canonicalize(FlowGraphOptimizer* optimizer) {
+Instruction* CheckSmiInstr::Canonicalize(FlowGraph* flow_graph) {
return (value()->Type()->ToCid() == kSmiCid) ? NULL : this;
}
-Instruction* CheckEitherNonSmiInstr::Canonicalize(
- FlowGraphOptimizer* optimizer) {
+Instruction* CheckEitherNonSmiInstr::Canonicalize(FlowGraph* flow_graph) {
if ((left()->Type()->ToCid() == kDoubleCid) ||
(right()->Type()->ToCid() == kDoubleCid)) {
return NULL; // Remove from the graph.
@@ -1494,6 +1490,17 @@
}
+LocationSummary* RedefinitionInstr::MakeLocationSummary() const {
+ UNREACHABLE();
+ return NULL;
+}
+
+
+void RedefinitionInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
+ UNREACHABLE();
+}
+
+
LocationSummary* ParameterInstr::MakeLocationSummary() const {
UNREACHABLE();
return NULL;
@@ -1526,6 +1533,17 @@
}
+LocationSummary* MaterializeObjectInstr::MakeLocationSummary() const {
+ UNREACHABLE();
+ return NULL;
+}
+
+
+void MaterializeObjectInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
+ UNREACHABLE();
+}
+
+
LocationSummary* StoreContextInstr::MakeLocationSummary() const {
const intptr_t kNumInputs = 1;
const intptr_t kNumTemps = 0;
diff --git a/runtime/vm/intermediate_language.h b/runtime/vm/intermediate_language.h
index dc33a3f..87220a8 100644
--- a/runtime/vm/intermediate_language.h
+++ b/runtime/vm/intermediate_language.h
@@ -22,8 +22,8 @@
class ControlInstruction;
class Definition;
class Environment;
+class FlowGraph;
class FlowGraphCompiler;
-class FlowGraphOptimizer;
class FlowGraphVisitor;
class Instruction;
class LocalVariable;
@@ -497,6 +497,7 @@
M(TargetEntry) \
M(CatchBlockEntry) \
M(Phi) \
+ M(Redefinition) \
M(Parameter) \
M(ParallelMove) \
M(PushArgument) \
@@ -534,6 +535,7 @@
M(LoadField) \
M(StoreVMField) \
M(LoadUntagged) \
+ M(LoadClassId) \
M(InstantiateTypeArguments) \
M(ExtractConstructorTypeArguments) \
M(ExtractConstructorInstantiator) \
@@ -584,7 +586,7 @@
M(Float32x4Clamp) \
M(Float32x4With) \
M(Float32x4ToUint32x4) \
-
+ M(MaterializeObject) \
#define FORWARD_DECLARATION(type) class type##Instr;
FOR_EACH_INSTRUCTION(FORWARD_DECLARATION)
@@ -764,7 +766,7 @@
// Returns a replacement for the instruction or NULL if the instruction can
// be eliminated. By default returns the this instruction which means no
// change.
- virtual Instruction* Canonicalize(FlowGraphOptimizer* optimizer);
+ virtual Instruction* Canonicalize(FlowGraph* flow_graph);
// Insert this instruction before 'next' after use lists are computed.
// Instructions cannot be inserted before a block entry or any other
@@ -876,6 +878,10 @@
friend class TargetEntryInstr;
friend class JoinEntryInstr;
friend class InstanceOfInstr;
+ friend class PolymorphicInstanceCallInstr;
+ friend class SmiToDoubleInstr;
+ friend class DoubleToIntegerInstr;
+ friend class BranchSimplifier;
virtual void RawSetInputAt(intptr_t i, Value* value) = 0;
@@ -1044,13 +1050,13 @@
intptr_t end_pos() const { return end_pos_; }
BlockEntryInstr* dominator() const { return dominator_; }
- void set_dominator(BlockEntryInstr* instr) { dominator_ = instr; }
const GrowableArray<BlockEntryInstr*>& dominated_blocks() {
return dominated_blocks_;
}
void AddDominatedBlock(BlockEntryInstr* block) {
+ block->set_dominator(this);
dominated_blocks_.Add(block);
}
void ClearDominatedBlocks() { dominated_blocks_.Clear(); }
@@ -1151,6 +1157,8 @@
virtual void ClearPredecessors() = 0;
virtual void AddPredecessor(BlockEntryInstr* predecessor) = 0;
+ void set_dominator(BlockEntryInstr* instr) { dominator_ = instr; }
+
intptr_t block_id_;
const intptr_t try_index_;
intptr_t preorder_number_;
@@ -1309,6 +1317,7 @@
// Classes that have access to predecessors_ when inlining.
friend class BlockEntryInstr;
friend class InlineExitCollector;
+ friend class PolymorphicInliner;
// Direct access to phis_ in order to resize it due to phi elimination.
friend class ConstantPropagator;
@@ -1542,7 +1551,7 @@
// Definitions can be canonicalized only into definitions to ensure
// this check statically we override base Canonicalize with a Canonicalize
// returning Definition (return type is covariant).
- virtual Definition* Canonicalize(FlowGraphOptimizer* optimizer);
+ virtual Definition* Canonicalize(FlowGraph* flow_graph);
static const intptr_t kReplacementMarker = -2;
@@ -1954,7 +1963,7 @@
void ReplaceWith(ComparisonInstr* other,
ForwardInstructionIterator* ignored);
- virtual Instruction* Canonicalize(FlowGraphOptimizer* optimizer);
+ virtual Instruction* Canonicalize(FlowGraph* flow_graph);
virtual void PrintTo(BufferFormatter* f) const;
@@ -2000,7 +2009,7 @@
SetInputAt(0, value);
}
- DECLARE_INSTRUCTION(StoreContext);
+ DECLARE_INSTRUCTION(StoreContext)
virtual intptr_t ArgumentCount() const { return 0; }
@@ -2046,6 +2055,28 @@
};
+class RedefinitionInstr : public TemplateDefinition<1> {
+ public:
+ explicit RedefinitionInstr(Value* value) {
+ SetInputAt(0, value);
+ }
+
+ DECLARE_INSTRUCTION(Redefinition)
+
+ Value* value() const { return inputs_[0]; }
+
+ virtual CompileType ComputeType() const;
+ virtual bool RecomputeType();
+
+ virtual bool CanDeoptimize() const { return false; }
+ virtual EffectSet Dependencies() const { return EffectSet::None(); }
+ virtual EffectSet Effects() const { return EffectSet::None(); }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(RedefinitionInstr);
+};
+
+
class RangeBoundary : public ValueObject {
public:
enum Kind { kUnknown, kSymbol, kConstant };
@@ -2275,7 +2306,7 @@
DECLARE_INSTRUCTION(Constant)
virtual CompileType ComputeType() const;
- virtual Definition* Canonicalize(FlowGraphOptimizer* optimizer);
+ virtual Definition* Canonicalize(FlowGraph* flow_graph);
const Object& value() const { return value_; }
@@ -2334,7 +2365,7 @@
virtual bool CanDeoptimize() const { return true; }
- virtual Definition* Canonicalize(FlowGraphOptimizer* optimizer);
+ virtual Definition* Canonicalize(FlowGraph* flow_graph);
virtual bool AllowsCSE() const { return true; }
virtual EffectSet Effects() const { return EffectSet::None(); }
@@ -2367,7 +2398,7 @@
virtual bool CanDeoptimize() const { return true; }
- virtual Definition* Canonicalize(FlowGraphOptimizer* optimizer);
+ virtual Definition* Canonicalize(FlowGraph* flow_graph);
virtual bool AllowsCSE() const { return true; }
virtual EffectSet Effects() const { return EffectSet::None(); }
@@ -2546,6 +2577,7 @@
ic_data_(ic_data),
with_checks_(with_checks) {
ASSERT(instance_call_ != NULL);
+ deopt_id_ = instance_call->deopt_id();
}
InstanceCallInstr* instance_call() const { return instance_call_; }
@@ -2675,7 +2707,7 @@
virtual bool CanDeoptimize() const { return false; }
- virtual Definition* Canonicalize(FlowGraphOptimizer* optimizer);
+ virtual Definition* Canonicalize(FlowGraph* flow_graph);
virtual void EmitBranchCode(FlowGraphCompiler* compiler,
BranchInstr* branch);
@@ -3132,7 +3164,7 @@
virtual bool CanDeoptimize() const { return true; }
- virtual Instruction* Canonicalize(FlowGraphOptimizer* optimizer);
+ virtual Instruction* Canonicalize(FlowGraph* flow_graph);
virtual void PrintOperandsTo(BufferFormatter* f) const;
@@ -3152,7 +3184,7 @@
public:
explicit LoadStaticFieldInstr(const Field& field) : field_(field) {}
- DECLARE_INSTRUCTION(LoadStaticField);
+ DECLARE_INSTRUCTION(LoadStaticField)
virtual CompileType ComputeType() const;
const Field& field() const { return field_; }
@@ -3181,7 +3213,7 @@
SetInputAt(0, value);
}
- DECLARE_INSTRUCTION(StoreStaticField);
+ DECLARE_INSTRUCTION(StoreStaticField)
virtual CompileType* ComputeInitialType() const;
const Field& field() const { return field_; }
@@ -3470,6 +3502,66 @@
};
+// This instruction captures the state of the object which had its allocation
+// removed during the AllocationSinking pass.
+// It does not produce any real code only deoptimization information.
+class MaterializeObjectInstr : public Definition {
+ public:
+ MaterializeObjectInstr(const Class& cls,
+ const ZoneGrowableArray<const Field*>& fields,
+ ZoneGrowableArray<Value*>* values)
+ : cls_(cls), fields_(fields), values_(values), locations_(NULL) {
+ ASSERT(fields_.length() == values_->length());
+ for (intptr_t i = 0; i < InputCount(); i++) {
+ InputAt(i)->set_instruction(this);
+ InputAt(i)->set_use_index(i);
+ }
+ }
+
+ const Class& cls() const { return cls_; }
+ const Field& FieldAt(intptr_t i) const {
+ return *fields_[i];
+ }
+ const Location& LocationAt(intptr_t i) {
+ return locations_[i];
+ }
+
+ DECLARE_INSTRUCTION(MaterializeObject)
+ virtual void PrintOperandsTo(BufferFormatter* f) const;
+
+ virtual intptr_t InputCount() const {
+ return values_->length();
+ }
+
+ virtual Value* InputAt(intptr_t i) const {
+ return (*values_)[i];
+ }
+
+ virtual bool CanDeoptimize() const { return false; }
+ virtual EffectSet Effects() const { return EffectSet::None(); }
+
+ LocationSummary* locs() {
+ UNREACHABLE();
+ return NULL;
+ }
+
+ Location* locations() { return locations_; }
+ void set_locations(Location* locations) { locations_ = locations; }
+
+ private:
+ virtual void RawSetInputAt(intptr_t i, Value* value) {
+ (*values_)[i] = value;
+ }
+
+ const Class& cls_;
+ const ZoneGrowableArray<const Field*>& fields_;
+ ZoneGrowableArray<Value*>* values_;
+ Location* locations_;
+
+ DISALLOW_COPY_AND_ASSIGN(MaterializeObjectInstr);
+};
+
+
class AllocateObjectWithBoundsCheckInstr : public TemplateDefinition<2> {
public:
AllocateObjectWithBoundsCheckInstr(ConstructorCallNode* node,
@@ -3574,7 +3666,7 @@
class LoadUntaggedInstr : public TemplateDefinition<1> {
public:
- explicit LoadUntaggedInstr(Value* object, intptr_t offset) : offset_(offset) {
+ LoadUntaggedInstr(Value* object, intptr_t offset) : offset_(offset) {
SetInputAt(0, object);
}
@@ -3604,6 +3696,34 @@
};
+class LoadClassIdInstr : public TemplateDefinition<1> {
+ public:
+ explicit LoadClassIdInstr(Value* object) {
+ SetInputAt(0, object);
+ }
+
+ virtual Representation representation() const {
+ return kTagged;
+ }
+ DECLARE_INSTRUCTION(LoadClassId)
+ virtual CompileType ComputeType() const;
+
+ Value* object() const { return inputs_[0]; }
+
+ virtual bool CanDeoptimize() const { return false; }
+
+ virtual bool AllowsCSE() const { return true; }
+ virtual EffectSet Effects() const { return EffectSet::None(); }
+ virtual EffectSet Dependencies() const {
+ return EffectSet::Externalization();
+ }
+ virtual bool AttributesEqual(Instruction* other) const { return true; }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(LoadClassIdInstr);
+};
+
+
class LoadFieldInstr : public TemplateDefinition<1> {
public:
LoadFieldInstr(Value* instance,
@@ -3652,7 +3772,7 @@
bool IsImmutableLengthLoad() const;
- virtual Definition* Canonicalize(FlowGraphOptimizer* optimizer);
+ virtual Definition* Canonicalize(FlowGraph* flow_graph);
static MethodRecognizer::Kind RecognizedKindFromArrayCid(intptr_t cid);
@@ -3824,7 +3944,7 @@
: token_pos_(token_pos),
num_context_variables_(num_context_variables) {}
- DECLARE_INSTRUCTION(AllocateContext);
+ DECLARE_INSTRUCTION(AllocateContext)
virtual CompileType ComputeType() const;
intptr_t token_pos() const { return token_pos_; }
@@ -3935,7 +4055,7 @@
virtual bool CanDeoptimize() const { return true; }
- virtual Instruction* Canonicalize(FlowGraphOptimizer* optimizer);
+ virtual Instruction* Canonicalize(FlowGraph* flow_graph);
virtual bool AllowsCSE() const { return true; }
virtual EffectSet Effects() const { return EffectSet::None(); }
@@ -3970,7 +4090,7 @@
virtual EffectSet Dependencies() const { return EffectSet::None(); }
virtual bool AttributesEqual(Instruction* other) const { return true; }
- Definition* Canonicalize(FlowGraphOptimizer* optimizer);
+ Definition* Canonicalize(FlowGraph* flow_graph);
private:
DISALLOW_COPY_AND_ASSIGN(BoxDoubleInstr);
@@ -4087,7 +4207,7 @@
virtual EffectSet Dependencies() const { return EffectSet::None(); }
virtual bool AttributesEqual(Instruction* other) const { return true; }
- Definition* Canonicalize(FlowGraphOptimizer* optimizer);
+ Definition* Canonicalize(FlowGraph* flow_graph);
private:
DISALLOW_COPY_AND_ASSIGN(UnboxDoubleInstr);
@@ -4137,8 +4257,6 @@
return (value()->Type()->ToCid() != kUint32x4Cid);
}
- virtual bool HasSideEffect() const { return false; }
-
virtual Representation representation() const {
return kUnboxedUint32x4;
}
@@ -4266,7 +4384,7 @@
DECLARE_INSTRUCTION(BinaryDoubleOp)
virtual CompileType ComputeType() const;
- virtual Definition* Canonicalize(FlowGraphOptimizer* optimizer);
+ virtual Definition* Canonicalize(FlowGraph* flow_graph);
virtual bool AllowsCSE() const { return true; }
virtual EffectSet Effects() const { return EffectSet::None(); }
@@ -4953,7 +5071,7 @@
return deopt_id_;
}
- virtual Definition* Canonicalize(FlowGraphOptimizer* optimizer);
+ virtual Definition* Canonicalize(FlowGraph* flow_graph);
DECLARE_INSTRUCTION(BinaryMintOp)
virtual CompileType ComputeType() const;
@@ -5125,7 +5243,7 @@
virtual void InferRange();
- virtual Definition* Canonicalize(FlowGraphOptimizer* optimizer);
+ virtual Definition* Canonicalize(FlowGraph* flow_graph);
// Returns true if right is a non-zero Smi constant which absolute value is
// a power of two.
@@ -5233,6 +5351,7 @@
DoubleToIntegerInstr(Value* value, InstanceCallInstr* instance_call)
: instance_call_(instance_call) {
SetInputAt(0, value);
+ deopt_id_ = instance_call->deopt_id();
}
Value* value() const { return inputs_[0]; }
@@ -5413,7 +5532,7 @@
const ICData& unary_checks() const { return unary_checks_; }
- virtual Instruction* Canonicalize(FlowGraphOptimizer* optimizer);
+ virtual Instruction* Canonicalize(FlowGraph* flow_graph);
virtual void PrintOperandsTo(BufferFormatter* f) const;
@@ -5451,7 +5570,7 @@
virtual bool CanDeoptimize() const { return true; }
- virtual Instruction* Canonicalize(FlowGraphOptimizer* optimizer);
+ virtual Instruction* Canonicalize(FlowGraph* flow_graph);
virtual bool AllowsCSE() const { return true; }
virtual EffectSet Effects() const { return EffectSet::None(); }
diff --git a/runtime/vm/intermediate_language_arm.cc b/runtime/vm/intermediate_language_arm.cc
index 710c2be..b87afd7 100644
--- a/runtime/vm/intermediate_language_arm.cc
+++ b/runtime/vm/intermediate_language_arm.cc
@@ -87,7 +87,7 @@
Label stack_ok;
__ Comment("Stack Check");
const intptr_t fp_sp_dist =
- (kFirstLocalSlotIndex + 1 - compiler->StackSize()) * kWordSize;
+ (kFirstLocalSlotFromFp + 1 - compiler->StackSize()) * kWordSize;
ASSERT(fp_sp_dist <= 0);
__ sub(R2, SP, ShifterOperand(FP));
__ CompareImmediate(R2, fp_sp_dist);
@@ -800,10 +800,10 @@
__ PushObject(Object::ZoneHandle());
// Pass a pointer to the first argument in R2.
if (!function().HasOptionalParameters()) {
- __ AddImmediate(R2, FP, (kLastParamSlotIndex +
- function().NumParameters() - 1) * kWordSize);
+ __ AddImmediate(R2, FP, (kParamEndSlotFromFp +
+ function().NumParameters()) * kWordSize);
} else {
- __ AddImmediate(R2, FP, kFirstLocalSlotIndex * kWordSize);
+ __ AddImmediate(R2, FP, kFirstLocalSlotFromFp * kWordSize);
}
// Compute the effective address. When running under the simulator,
// this is a redirection address that forces the simulator to call
@@ -844,6 +844,17 @@
}
+LocationSummary* LoadClassIdInstr::MakeLocationSummary() const {
+ UNIMPLEMENTED();
+ return NULL;
+}
+
+
+void LoadClassIdInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
+ UNIMPLEMENTED();
+}
+
+
CompileType LoadIndexedInstr::ComputeType() const {
switch (class_id_) {
case kArrayCid:
@@ -1717,7 +1728,7 @@
// Restore SP from FP as we are coming from a throw and the code for
// popping arguments has not been run.
const intptr_t fp_sp_dist =
- (kFirstLocalSlotIndex + 1 - compiler->StackSize()) * kWordSize;
+ (kFirstLocalSlotFromFp + 1 - compiler->StackSize()) * kWordSize;
ASSERT(fp_sp_dist <= 0);
__ AddImmediate(SP, FP, fp_sp_dist);
@@ -2299,7 +2310,7 @@
void PolymorphicInstanceCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
- Label* deopt = compiler->AddDeoptStub(instance_call()->deopt_id(),
+ Label* deopt = compiler->AddDeoptStub(deopt_id(),
kDeoptPolymorphicInstanceCallTestFail);
if (ic_data().NumberOfChecks() == 0) {
__ b(deopt);
@@ -2309,7 +2320,7 @@
if (!with_checks()) {
ASSERT(ic_data().HasOneTarget());
const Function& target = Function::ZoneHandle(ic_data().GetTargetAt(0));
- compiler->GenerateStaticCall(instance_call()->deopt_id(),
+ compiler->GenerateStaticCall(deopt_id(),
instance_call()->token_pos(),
target,
instance_call()->ArgumentCount(),
@@ -2329,7 +2340,7 @@
instance_call()->ArgumentCount(),
instance_call()->argument_names(),
deopt,
- instance_call()->deopt_id(),
+ deopt_id(),
instance_call()->token_pos(),
locs());
}
@@ -2789,4 +2800,3 @@
} // namespace dart
#endif // defined TARGET_ARCH_ARM
-
diff --git a/runtime/vm/intermediate_language_ia32.cc b/runtime/vm/intermediate_language_ia32.cc
index 20095ed..42ef7c0 100644
--- a/runtime/vm/intermediate_language_ia32.cc
+++ b/runtime/vm/intermediate_language_ia32.cc
@@ -85,7 +85,7 @@
__ Comment("Stack Check");
Label done;
const intptr_t fp_sp_dist =
- (kFirstLocalSlotIndex + 1 - compiler->StackSize()) * kWordSize;
+ (kFirstLocalSlotFromFp + 1 - compiler->StackSize()) * kWordSize;
ASSERT(fp_sp_dist <= 0);
__ movl(EDI, ESP);
__ subl(EDI, EBP);
@@ -1048,10 +1048,10 @@
__ PushObject(Object::ZoneHandle());
// Pass a pointer to the first argument in EAX.
if (!function().HasOptionalParameters()) {
- __ leal(EAX, Address(EBP, (kLastParamSlotIndex +
- function().NumParameters() - 1) * kWordSize));
+ __ leal(EAX, Address(EBP, (kParamEndSlotFromFp +
+ function().NumParameters()) * kWordSize));
} else {
- __ leal(EAX, Address(EBP, kFirstLocalSlotIndex * kWordSize));
+ __ leal(EAX, Address(EBP, kFirstLocalSlotFromFp * kWordSize));
}
__ movl(ECX, Immediate(reinterpret_cast<uword>(native_c_function())));
__ movl(EDX, Immediate(NativeArguments::ComputeArgcTag(function())));
@@ -1077,13 +1077,10 @@
LocationSummary* StringFromCharCodeInstr::MakeLocationSummary() const {
const intptr_t kNumInputs = 1;
- const intptr_t kNumTemps = 0;
- LocationSummary* locs =
- new LocationSummary(kNumInputs, kNumTemps, LocationSummary::kNoCall);
// TODO(fschneider): Allow immediate operands for the char code.
- locs->set_in(0, Location::RequiresRegister());
- locs->set_out(Location::RequiresRegister());
- return locs;
+ return LocationSummary::Make(kNumInputs,
+ Location::RequiresRegister(),
+ LocationSummary::kNoCall);
}
@@ -1101,12 +1098,9 @@
LocationSummary* LoadUntaggedInstr::MakeLocationSummary() const {
const intptr_t kNumInputs = 1;
- const intptr_t kNumTemps = 0;
- LocationSummary* locs =
- new LocationSummary(kNumInputs, kNumTemps, LocationSummary::kNoCall);
- locs->set_in(0, Location::RequiresRegister());
- locs->set_out(Location::RequiresRegister());
- return locs;
+ return LocationSummary::Make(kNumInputs,
+ Location::RequiresRegister(),
+ LocationSummary::kNoCall);
}
@@ -1117,6 +1111,29 @@
}
+LocationSummary* LoadClassIdInstr::MakeLocationSummary() const {
+ const intptr_t kNumInputs = 1;
+ return LocationSummary::Make(kNumInputs,
+ Location::RequiresRegister(),
+ LocationSummary::kNoCall);
+}
+
+
+void LoadClassIdInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
+ Register object = locs()->in(0).reg();
+ Register result = locs()->out().reg();
+ Label load, done;
+ __ testl(object, Immediate(kSmiTagMask));
+ __ j(NOT_ZERO, &load, Assembler::kNearJump);
+ __ movl(result, Immediate(Smi::RawValue(kSmiCid)));
+ __ jmp(&done);
+ __ Bind(&load);
+ __ LoadClassId(result, object);
+ __ SmiTag(result);
+ __ Bind(&done);
+}
+
+
CompileType LoadIndexedInstr::ComputeType() const {
switch (class_id_) {
case kArrayCid:
@@ -2065,7 +2082,7 @@
// Restore ESP from EBP as we are coming from a throw and the code for
// popping arguments has not been run.
const intptr_t fp_sp_dist =
- (kFirstLocalSlotIndex + 1 - compiler->StackSize()) * kWordSize;
+ (kFirstLocalSlotFromFp + 1 - compiler->StackSize()) * kWordSize;
ASSERT(fp_sp_dist <= 0);
__ leal(ESP, Address(EBP, fp_sp_dist));
@@ -3317,12 +3334,9 @@
LocationSummary* UnarySmiOpInstr::MakeLocationSummary() const {
const intptr_t kNumInputs = 1;
- const intptr_t kNumTemps = 0;
- LocationSummary* summary =
- new LocationSummary(kNumInputs, kNumTemps, LocationSummary::kNoCall);
- summary->set_in(0, Location::RequiresRegister());
- summary->set_out(Location::SameAsFirstInput());
- return summary;
+ return LocationSummary::Make(kNumInputs,
+ Location::SameAsFirstInput(),
+ LocationSummary::kNoCall);
}
@@ -3400,7 +3414,7 @@
const Function& target = Function::ZoneHandle(ic_data.GetTargetAt(0));
const intptr_t kNumberOfArguments = 1;
- compiler->GenerateStaticCall(instance_call()->deopt_id(),
+ compiler->GenerateStaticCall(deopt_id(),
instance_call()->token_pos(),
target,
kNumberOfArguments,
@@ -3496,7 +3510,7 @@
void PolymorphicInstanceCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
- Label* deopt = compiler->AddDeoptStub(instance_call()->deopt_id(),
+ Label* deopt = compiler->AddDeoptStub(deopt_id(),
kDeoptPolymorphicInstanceCallTestFail);
if (ic_data().NumberOfChecks() == 0) {
__ jmp(deopt);
@@ -3506,7 +3520,7 @@
if (!with_checks()) {
ASSERT(ic_data().HasOneTarget());
const Function& target = Function::ZoneHandle(ic_data().GetTargetAt(0));
- compiler->GenerateStaticCall(instance_call()->deopt_id(),
+ compiler->GenerateStaticCall(deopt_id(),
instance_call()->token_pos(),
target,
instance_call()->ArgumentCount(),
@@ -3527,7 +3541,7 @@
instance_call()->ArgumentCount(),
instance_call()->argument_names(),
deopt,
- instance_call()->deopt_id(),
+ deopt_id(),
instance_call()->token_pos(),
locs());
}
diff --git a/runtime/vm/intermediate_language_mips.cc b/runtime/vm/intermediate_language_mips.cc
index cee62a3..e33d78b 100644
--- a/runtime/vm/intermediate_language_mips.cc
+++ b/runtime/vm/intermediate_language_mips.cc
@@ -90,7 +90,7 @@
__ Comment("Stack Check");
__ TraceSimMsg("Stack Check");
const intptr_t fp_sp_dist =
- (kFirstLocalSlotIndex + 1 - compiler->StackSize()) * kWordSize;
+ (kFirstLocalSlotFromFp + 1 - compiler->StackSize()) * kWordSize;
ASSERT(fp_sp_dist <= 0);
__ subu(T2, SP, FP);
@@ -814,10 +814,10 @@
__ PushObject(Object::ZoneHandle());
// Pass a pointer to the first argument in A2.
if (!function().HasOptionalParameters()) {
- __ AddImmediate(A2, FP, (kLastParamSlotIndex +
- function().NumParameters() - 1) * kWordSize);
+ __ AddImmediate(A2, FP, (kParamEndSlotFromFp +
+ function().NumParameters()) * kWordSize);
} else {
- __ AddImmediate(A2, FP, kFirstLocalSlotIndex * kWordSize);
+ __ AddImmediate(A2, FP, kFirstLocalSlotFromFp * kWordSize);
}
// Compute the effective address. When running under the simulator,
// this is a redirection address that forces the simulator to call
@@ -858,6 +858,17 @@
}
+LocationSummary* LoadClassIdInstr::MakeLocationSummary() const {
+ UNIMPLEMENTED();
+ return NULL;
+}
+
+
+void LoadClassIdInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
+ UNIMPLEMENTED();
+}
+
+
CompileType LoadIndexedInstr::ComputeType() const {
switch (class_id_) {
case kArrayCid:
@@ -1665,7 +1676,7 @@
// Restore SP from FP as we are coming from a throw and the code for
// popping arguments has not been run.
const intptr_t fp_sp_dist =
- (kFirstLocalSlotIndex + 1 - compiler->StackSize()) * kWordSize;
+ (kFirstLocalSlotFromFp + 1 - compiler->StackSize()) * kWordSize;
ASSERT(fp_sp_dist <= 0);
__ AddImmediate(SP, FP, fp_sp_dist);
@@ -2274,7 +2285,7 @@
void PolymorphicInstanceCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
__ TraceSimMsg("PolymorphicInstanceCallInstr");
- Label* deopt = compiler->AddDeoptStub(instance_call()->deopt_id(),
+ Label* deopt = compiler->AddDeoptStub(deopt_id(),
kDeoptPolymorphicInstanceCallTestFail);
if (ic_data().NumberOfChecks() == 0) {
__ b(deopt);
@@ -2284,7 +2295,7 @@
if (!with_checks()) {
ASSERT(ic_data().HasOneTarget());
const Function& target = Function::ZoneHandle(ic_data().GetTargetAt(0));
- compiler->GenerateStaticCall(instance_call()->deopt_id(),
+ compiler->GenerateStaticCall(deopt_id(),
instance_call()->token_pos(),
target,
instance_call()->ArgumentCount(),
@@ -2304,7 +2315,7 @@
instance_call()->ArgumentCount(),
instance_call()->argument_names(),
deopt,
- instance_call()->deopt_id(),
+ deopt_id(),
instance_call()->token_pos(),
locs());
}
@@ -2820,4 +2831,3 @@
} // namespace dart
#endif // defined TARGET_ARCH_MIPS
-
diff --git a/runtime/vm/intermediate_language_x64.cc b/runtime/vm/intermediate_language_x64.cc
index 989c41b..d01c003 100644
--- a/runtime/vm/intermediate_language_x64.cc
+++ b/runtime/vm/intermediate_language_x64.cc
@@ -85,7 +85,7 @@
__ Comment("Stack Check");
Label done;
const intptr_t fp_sp_dist =
- (kFirstLocalSlotIndex + 1 - compiler->StackSize()) * kWordSize;
+ (kFirstLocalSlotFromFp + 1 - compiler->StackSize()) * kWordSize;
ASSERT(fp_sp_dist <= 0);
__ movq(RDI, RSP);
__ subq(RDI, RBP);
@@ -1059,11 +1059,11 @@
__ PushObject(Object::ZoneHandle());
// Pass a pointer to the first argument in RAX.
if (!function().HasOptionalParameters()) {
- __ leaq(RAX, Address(RBP, (kLastParamSlotIndex +
- function().NumParameters() - 1) * kWordSize));
+ __ leaq(RAX, Address(RBP, (kParamEndSlotFromFp +
+ function().NumParameters()) * kWordSize));
} else {
__ leaq(RAX,
- Address(RBP, kFirstLocalSlotIndex * kWordSize));
+ Address(RBP, kFirstLocalSlotFromFp * kWordSize));
}
__ movq(RBX, Immediate(reinterpret_cast<uword>(native_c_function())));
__ movq(R10, Immediate(NativeArguments::ComputeArgcTag(function())));
@@ -1089,13 +1089,10 @@
LocationSummary* StringFromCharCodeInstr::MakeLocationSummary() const {
const intptr_t kNumInputs = 1;
- const intptr_t kNumTemps = 0;
- LocationSummary* locs =
- new LocationSummary(kNumInputs, kNumTemps, LocationSummary::kNoCall);
// TODO(fschneider): Allow immediate operands for the char code.
- locs->set_in(0, Location::RequiresRegister());
- locs->set_out(Location::RequiresRegister());
- return locs;
+ return LocationSummary::Make(kNumInputs,
+ Location::RequiresRegister(),
+ LocationSummary::kNoCall);
}
@@ -1113,12 +1110,9 @@
LocationSummary* LoadUntaggedInstr::MakeLocationSummary() const {
const intptr_t kNumInputs = 1;
- const intptr_t kNumTemps = 0;
- LocationSummary* locs =
- new LocationSummary(kNumInputs, kNumTemps, LocationSummary::kNoCall);
- locs->set_in(0, Location::RequiresRegister());
- locs->set_out(Location::RequiresRegister());
- return locs;
+ return LocationSummary::Make(kNumInputs,
+ Location::RequiresRegister(),
+ LocationSummary::kNoCall);
}
@@ -1129,6 +1123,29 @@
}
+LocationSummary* LoadClassIdInstr::MakeLocationSummary() const {
+ const intptr_t kNumInputs = 1;
+ return LocationSummary::Make(kNumInputs,
+ Location::RequiresRegister(),
+ LocationSummary::kNoCall);
+}
+
+
+void LoadClassIdInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
+ Register object = locs()->in(0).reg();
+ Register result = locs()->out().reg();
+ Label load, done;
+ __ testq(object, Immediate(kSmiTagMask));
+ __ j(NOT_ZERO, &load, Assembler::kNearJump);
+ __ movq(result, Immediate(Smi::RawValue(kSmiCid)));
+ __ jmp(&done);
+ __ Bind(&load);
+ __ LoadClassId(result, object);
+ __ SmiTag(result);
+ __ Bind(&done);
+}
+
+
CompileType LoadIndexedInstr::ComputeType() const {
switch (class_id_) {
case kArrayCid:
@@ -2035,7 +2052,7 @@
// Restore RSP from RBP as we are coming from a throw and the code for
// popping arguments has not been run.
const intptr_t fp_sp_dist =
- (kFirstLocalSlotIndex + 1 - compiler->StackSize()) * kWordSize;
+ (kFirstLocalSlotFromFp + 1 - compiler->StackSize()) * kWordSize;
ASSERT(fp_sp_dist <= 0);
__ leaq(RSP, Address(RBP, fp_sp_dist));
@@ -2747,12 +2764,9 @@
LocationSummary* UnboxFloat32x4Instr::MakeLocationSummary() const {
const intptr_t kNumInputs = 1;
- const intptr_t kNumTemps = 0;
- LocationSummary* summary =
- new LocationSummary(kNumInputs, kNumTemps, LocationSummary::kNoCall);
- summary->set_in(0, Location::RequiresRegister());
- summary->set_out(Location::RequiresFpuRegister());
- return summary;
+ return LocationSummary::Make(kNumInputs,
+ Location::RequiresFpuRegister(),
+ LocationSummary::kNoCall);
}
@@ -3324,12 +3338,9 @@
LocationSummary* UnarySmiOpInstr::MakeLocationSummary() const {
const intptr_t kNumInputs = 1;
- const intptr_t kNumTemps = 0;
- LocationSummary* summary =
- new LocationSummary(kNumInputs, kNumTemps, LocationSummary::kNoCall);
- summary->set_in(0, Location::RequiresRegister());
- summary->set_out(Location::SameAsFirstInput());
- return summary;
+ return LocationSummary::Make(kNumInputs,
+ Location::SameAsFirstInput(),
+ LocationSummary::kNoCall);
}
@@ -3411,7 +3422,7 @@
const intptr_t kNumberOfArguments = 1;
__ pushq(value_obj);
- compiler->GenerateStaticCall(instance_call()->deopt_id(),
+ compiler->GenerateStaticCall(deopt_id(),
instance_call()->token_pos(),
target,
kNumberOfArguments,
@@ -3520,7 +3531,7 @@
void PolymorphicInstanceCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
- Label* deopt = compiler->AddDeoptStub(instance_call()->deopt_id(),
+ Label* deopt = compiler->AddDeoptStub(deopt_id(),
kDeoptPolymorphicInstanceCallTestFail);
if (ic_data().NumberOfChecks() == 0) {
__ jmp(deopt);
@@ -3530,7 +3541,7 @@
if (!with_checks()) {
ASSERT(ic_data().HasOneTarget());
const Function& target = Function::ZoneHandle(ic_data().GetTargetAt(0));
- compiler->GenerateStaticCall(instance_call()->deopt_id(),
+ compiler->GenerateStaticCall(deopt_id(),
instance_call()->token_pos(),
target,
instance_call()->ArgumentCount(),
@@ -3549,7 +3560,7 @@
instance_call()->ArgumentCount(),
instance_call()->argument_names(),
deopt,
- instance_call()->deopt_id(),
+ deopt_id(),
instance_call()->token_pos(),
locs());
}
diff --git a/runtime/vm/isolate.cc b/runtime/vm/isolate.cc
index 473f001..d445f17 100644
--- a/runtime/vm/isolate.cc
+++ b/runtime/vm/isolate.cc
@@ -311,6 +311,56 @@
}
+void DeferredObjectRef::Materialize() {
+ DeferredObject* obj = Isolate::Current()->GetDeferredObject(index());
+ *slot() = obj->object();
+ if (FLAG_trace_deoptimization_verbose) {
+ OS::PrintErr("writing instance ref at %"Px": %s\n",
+ reinterpret_cast<uword>(slot()),
+ Instance::Handle(obj->object()).ToCString());
+ }
+}
+
+
+RawInstance* DeferredObject::object() {
+ if (object_ == NULL) {
+ Materialize();
+ }
+ return object_->raw();
+}
+
+
+void DeferredObject::Materialize() {
+ Class& cls = Class::Handle();
+ cls ^= GetClass();
+
+ if (FLAG_trace_deoptimization_verbose) {
+ OS::PrintErr("materializing instance of %s (%"Px", %"Pd" fields)\n",
+ cls.ToCString(),
+ reinterpret_cast<uword>(args_),
+ field_count_);
+ }
+
+ const Instance& obj = Instance::ZoneHandle(Instance::New(cls));
+
+ Field& field = Field::Handle();
+ Object& value = Object::Handle();
+ for (intptr_t i = 0; i < field_count_; i++) {
+ field ^= GetField(i);
+ value = GetValue(i);
+ obj.SetField(field, value);
+
+ if (FLAG_trace_deoptimization_verbose) {
+ OS::PrintErr(" %s <- %s\n",
+ String::Handle(field.name()).ToCString(),
+ value.ToCString());
+ }
+ }
+
+ object_ = &obj;
+}
+
+
Isolate::Isolate()
: store_buffer_(),
message_notify_callback_(NULL),
@@ -344,6 +394,9 @@
deopt_fpu_registers_copy_(NULL),
deopt_frame_copy_(NULL),
deopt_frame_copy_size_(0),
+ deferred_boxes_(NULL),
+ deferred_object_refs_(NULL),
+ deferred_objects_count_(0),
deferred_objects_(NULL),
stacktrace_(NULL),
stack_frame_index_(-1) {
@@ -994,6 +1047,31 @@
}
+static void FillDeferredSlots(DeferredSlot** slot_list) {
+ DeferredSlot* slot = *slot_list;
+ *slot_list = NULL;
+
+ while (slot != NULL) {
+ DeferredSlot* current = slot;
+ slot = slot->next();
+
+ current->Materialize();
+
+ delete current;
+ }
+}
+
+
+void Isolate::MaterializeDeferredBoxes() {
+ FillDeferredSlots(&deferred_boxes_);
+}
+
+
+void Isolate::MaterializeDeferredObjects() {
+ FillDeferredSlots(&deferred_object_refs_);
+}
+
+
static char* GetRootScriptUri(Isolate* isolate) {
const Library& library =
Library::Handle(isolate->object_store()->root_library());
diff --git a/runtime/vm/isolate.h b/runtime/vm/isolate.h
index c87b56d..407a9f8 100644
--- a/runtime/vm/isolate.h
+++ b/runtime/vm/isolate.h
@@ -26,6 +26,7 @@
class HandleVisitor;
class Heap;
class ICData;
+class Instance;
class LongJump;
class MessageHandler;
class Mutex;
@@ -49,30 +50,31 @@
// Used by the deoptimization infrastructure to defer allocation of unboxed
// objects until frame is fully rewritten and GC is safe.
-// See callers of Isolate::DeferObjectMaterialization.
-class DeferredObject {
+// Describes a stack slot that should be populated with a reference to the
+// materialized object.
+class DeferredSlot {
public:
- DeferredObject(RawInstance** slot, DeferredObject* next)
+ DeferredSlot(RawInstance** slot, DeferredSlot* next)
: slot_(slot), next_(next) { }
- virtual ~DeferredObject() { }
+ virtual ~DeferredSlot() { }
RawInstance** slot() const { return slot_; }
- DeferredObject* next() const { return next_; }
+ DeferredSlot* next() const { return next_; }
virtual void Materialize() = 0;
private:
RawInstance** const slot_;
- DeferredObject* const next_;
+ DeferredSlot* const next_;
- DISALLOW_COPY_AND_ASSIGN(DeferredObject);
+ DISALLOW_COPY_AND_ASSIGN(DeferredSlot);
};
-class DeferredDouble : public DeferredObject {
+class DeferredDouble : public DeferredSlot {
public:
- DeferredDouble(double value, RawInstance** slot, DeferredObject* next)
- : DeferredObject(slot, next), value_(value) { }
+ DeferredDouble(double value, RawInstance** slot, DeferredSlot* next)
+ : DeferredSlot(slot, next), value_(value) { }
virtual void Materialize();
@@ -85,10 +87,10 @@
};
-class DeferredMint : public DeferredObject {
+class DeferredMint : public DeferredSlot {
public:
- DeferredMint(int64_t value, RawInstance** slot, DeferredObject* next)
- : DeferredObject(slot, next), value_(value) { }
+ DeferredMint(int64_t value, RawInstance** slot, DeferredSlot* next)
+ : DeferredSlot(slot, next), value_(value) { }
virtual void Materialize();
@@ -101,11 +103,11 @@
};
-class DeferredFloat32x4 : public DeferredObject {
+class DeferredFloat32x4 : public DeferredSlot {
public:
DeferredFloat32x4(simd128_value_t value, RawInstance** slot,
- DeferredObject* next)
- : DeferredObject(slot, next), value_(value) { }
+ DeferredSlot* next)
+ : DeferredSlot(slot, next), value_(value) { }
virtual void Materialize();
@@ -118,11 +120,11 @@
};
-class DeferredUint32x4 : public DeferredObject {
+class DeferredUint32x4 : public DeferredSlot {
public:
DeferredUint32x4(simd128_value_t value, RawInstance** slot,
- DeferredObject* next)
- : DeferredObject(slot, next), value_(value) { }
+ DeferredSlot* next)
+ : DeferredSlot(slot, next), value_(value) { }
virtual void Materialize();
@@ -135,6 +137,86 @@
};
+// Describes a slot that contains a reference to an object that had its
+// allocation removed by AllocationSinking pass.
+// Object itself is described and materialized by DeferredObject.
+class DeferredObjectRef : public DeferredSlot {
+ public:
+ DeferredObjectRef(intptr_t index, RawInstance** slot, DeferredSlot* next)
+ : DeferredSlot(slot, next), index_(index) { }
+
+ virtual void Materialize();
+
+ intptr_t index() const { return index_; }
+
+ private:
+ const intptr_t index_;
+
+ DISALLOW_COPY_AND_ASSIGN(DeferredObjectRef);
+};
+
+
+// Describes an object which allocation was removed by AllocationSinking pass.
+// Arguments for materialization are stored as a part of expression stack
+// for the bottommost deoptimized frame so that GC could discover them.
+// They will be removed from the stack at the very end of deoptimization.
+class DeferredObject {
+ public:
+ DeferredObject(intptr_t field_count, intptr_t* args)
+ : field_count_(field_count),
+ args_(reinterpret_cast<RawObject**>(args)),
+ object_(NULL) { }
+
+ intptr_t ArgumentCount() const {
+ return kFieldsStartIndex + kFieldEntrySize * field_count_;
+ }
+
+ RawInstance* object();
+
+ private:
+ enum {
+ kClassIndex = 0,
+ kFieldsStartIndex = kClassIndex + 1
+ };
+
+ enum {
+ kFieldIndex = 0,
+ kValueIndex,
+ kFieldEntrySize,
+ };
+
+ // Materializes the object. Returns amount of values that were consumed
+ // and should be removed from the expression stack at the very end of
+ // deoptimization.
+ void Materialize();
+
+ RawObject* GetClass() const {
+ return args_[kClassIndex];
+ }
+
+ RawObject* GetField(intptr_t index) const {
+ return args_[kFieldsStartIndex + kFieldEntrySize * index + kFieldIndex];
+ }
+
+ RawObject* GetValue(intptr_t index) const {
+ return args_[kFieldsStartIndex + kFieldEntrySize * index + kValueIndex];
+ }
+
+ // Amount of fields that have to be initialized.
+ const intptr_t field_count_;
+
+ // Pointer to the first materialization argument on the stack.
+ // The first argument is Class of the instance to materialize followed by
+ // Field, value pairs.
+ RawObject** args_;
+
+ // Object materialized from this description.
+ const Instance* object_;
+
+ DISALLOW_COPY_AND_ASSIGN(DeferredObject);
+};
+
+
class Isolate : public BaseIsolate {
public:
~Isolate();
@@ -424,40 +506,78 @@
}
intptr_t deopt_frame_copy_size() const { return deopt_frame_copy_size_; }
+ void PrepareForDeferredMaterialization(intptr_t count) {
+ if (count > 0) {
+ deferred_objects_ = new DeferredObject*[count];
+ deferred_objects_count_ = count;
+ }
+ }
+
+ void DeleteDeferredObjects() {
+ for (intptr_t i = 0; i < deferred_objects_count_; i++) {
+ delete deferred_objects_[i];
+ }
+ delete[] deferred_objects_;
+ deferred_objects_ = NULL;
+ deferred_objects_count_ = 0;
+ }
+
+ DeferredObject* GetDeferredObject(intptr_t idx) const {
+ return deferred_objects_[idx];
+ }
+
+ void SetDeferredObjectAt(intptr_t idx, DeferredObject* object) {
+ deferred_objects_[idx] = object;
+ }
+
+ intptr_t DeferredObjectsCount() const {
+ return deferred_objects_count_;
+ }
+
+ void DeferMaterializedObjectRef(intptr_t idx, intptr_t* slot) {
+ deferred_object_refs_ = new DeferredObjectRef(
+ idx,
+ reinterpret_cast<RawInstance**>(slot),
+ deferred_object_refs_);
+ }
+
void DeferDoubleMaterialization(double value, RawDouble** slot) {
- deferred_objects_ = new DeferredDouble(
+ deferred_boxes_ = new DeferredDouble(
value,
reinterpret_cast<RawInstance**>(slot),
- deferred_objects_);
+ deferred_boxes_);
}
void DeferMintMaterialization(int64_t value, RawMint** slot) {
- deferred_objects_ = new DeferredMint(value,
- reinterpret_cast<RawInstance**>(slot),
- deferred_objects_);
+ deferred_boxes_ = new DeferredMint(
+ value,
+ reinterpret_cast<RawInstance**>(slot),
+ deferred_boxes_);
}
void DeferFloat32x4Materialization(simd128_value_t value,
RawFloat32x4** slot) {
- deferred_objects_ = new DeferredFloat32x4(
+ deferred_boxes_ = new DeferredFloat32x4(
value,
reinterpret_cast<RawInstance**>(slot),
- deferred_objects_);
+ deferred_boxes_);
}
void DeferUint32x4Materialization(simd128_value_t value,
RawUint32x4** slot) {
- deferred_objects_ = new DeferredUint32x4(
+ deferred_boxes_ = new DeferredUint32x4(
value,
reinterpret_cast<RawInstance**>(slot),
- deferred_objects_);
+ deferred_boxes_);
}
- DeferredObject* DetachDeferredObjects() {
- DeferredObject* list = deferred_objects_;
- deferred_objects_ = NULL;
- return list;
- }
+ // Populate all deferred slots that contain boxes for double, mint, simd
+ // values.
+ void MaterializeDeferredBoxes();
+
+ // Populate all slots containing references to objects which allocations
+ // were eliminated by AllocationSinking pass.
+ void MaterializeDeferredObjects();
static char* GetStatus(const char* request);
@@ -511,7 +631,11 @@
fpu_register_t* deopt_fpu_registers_copy_;
intptr_t* deopt_frame_copy_;
intptr_t deopt_frame_copy_size_;
- DeferredObject* deferred_objects_;
+ DeferredSlot* deferred_boxes_;
+ DeferredSlot* deferred_object_refs_;
+
+ intptr_t deferred_objects_count_;
+ DeferredObject** deferred_objects_;
// Status support.
char* stacktrace_;
diff --git a/runtime/vm/locations.cc b/runtime/vm/locations.cc
index e1a86bc..a80f5ff 100644
--- a/runtime/vm/locations.cc
+++ b/runtime/vm/locations.cc
@@ -90,10 +90,10 @@
Address Location::ToStackSlotAddress() const {
const intptr_t index = stack_index();
if (index < 0) {
- const intptr_t offset = (kLastParamSlotIndex - index - 1) * kWordSize;
+ const intptr_t offset = (kParamEndSlotFromFp - index) * kWordSize;
return Address(FPREG, offset);
} else {
- const intptr_t offset = (kFirstLocalSlotIndex - index) * kWordSize;
+ const intptr_t offset = (kFirstLocalSlotFromFp - index) * kWordSize;
return Address(FPREG, offset);
}
}
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index aa85a51..1a095e6 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -7528,6 +7528,15 @@
}
+intptr_t DeoptInfo::FrameSize() const {
+ intptr_t pos = 0;
+ while (Instruction(pos) == DeoptInstr::kMaterializeObject) {
+ pos++;
+ }
+ return TranslationLength() - pos;
+}
+
+
intptr_t DeoptInfo::TranslationLength() const {
intptr_t length = Length();
if (Instruction(length - 1) != DeoptInstr::kSuffix) return length;
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 19802e7..6d3a70f 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -2596,9 +2596,17 @@
};
-// Holds deopt information at one deoptimization point. The information
-// is a list of DeoptInstr objects, specifying transformation information
-// for each slot in unoptimized frame(s).
+// Holds deopt information at one deoptimization point. The information consists
+// of two parts:
+// - first a prefix consiting of kMaterializeObject instructions describing
+// objects which had their allocation removed as part of AllocationSinking
+// pass and have to be materialized;
+// - followed by a list of DeoptInstr objects, specifying transformation
+// information for each slot in unoptimized frame(s).
+// Arguments for object materialization (class of instance to be allocated and
+// field-value pairs) are added as artificial slots to the expression stack
+// of the bottom-most frame. They are removed from the stack at the very end
+// of deoptimization by the deoptimization stub.
class DeoptInfo : public Object {
private:
// Describes the layout of deopt info data. The index of a deopt-info entry
@@ -2617,6 +2625,10 @@
// deoptimization translation.
intptr_t TranslationLength() const;
+ // Size of the frame part of the translation not counting kMaterializeObject
+ // instructions in the prefix.
+ intptr_t FrameSize() const;
+
static RawDeoptInfo* New(intptr_t num_commands);
static const intptr_t kBytesPerElement = (kNumberOfEntries * kWordSize);
diff --git a/runtime/vm/os.h b/runtime/vm/os.h
index a41e3bd..92c0c3b 100644
--- a/runtime/vm/os.h
+++ b/runtime/vm/os.h
@@ -18,6 +18,9 @@
// Interface to the underlying OS platform.
class OS {
public:
+ // Returns the name of the given OS. For example "linux".
+ static const char* Name();
+
// Returns the abbreviated time-zone name for the given instant.
// For example "CET" or "CEST".
static const char* GetTimeZoneName(int64_t seconds_since_epoch);
diff --git a/runtime/vm/os_android.cc b/runtime/vm/os_android.cc
index ec6413d..17a378a 100644
--- a/runtime/vm/os_android.cc
+++ b/runtime/vm/os_android.cc
@@ -184,6 +184,11 @@
};
+const char* OS::Name() {
+ return "android";
+}
+
+
static bool LocalTime(int64_t seconds_since_epoch, tm* tm_result) {
time_t seconds = static_cast<time_t>(seconds_since_epoch);
if (seconds != seconds_since_epoch) return false;
diff --git a/runtime/vm/os_linux.cc b/runtime/vm/os_linux.cc
index d6ddd80..2b869d4 100644
--- a/runtime/vm/os_linux.cc
+++ b/runtime/vm/os_linux.cc
@@ -268,6 +268,11 @@
};
+const char* OS::Name() {
+ return "linux";
+}
+
+
static bool LocalTime(int64_t seconds_since_epoch, tm* tm_result) {
time_t seconds = static_cast<time_t>(seconds_since_epoch);
if (seconds != seconds_since_epoch) return false;
diff --git a/runtime/vm/os_macos.cc b/runtime/vm/os_macos.cc
index cd0b53e..62c9376 100644
--- a/runtime/vm/os_macos.cc
+++ b/runtime/vm/os_macos.cc
@@ -21,6 +21,11 @@
namespace dart {
+const char* OS::Name() {
+ return "macos";
+}
+
+
static bool LocalTime(int64_t seconds_since_epoch, tm* tm_result) {
time_t seconds = static_cast<time_t>(seconds_since_epoch);
if (seconds != seconds_since_epoch) return false;
diff --git a/runtime/vm/os_win.cc b/runtime/vm/os_win.cc
index bdd6cec..b69dd83 100644
--- a/runtime/vm/os_win.cc
+++ b/runtime/vm/os_win.cc
@@ -16,6 +16,11 @@
namespace dart {
+const char* OS::Name() {
+ return "windows";
+}
+
+
// As a side-effect sets the globals _timezone, _daylight and _tzname.
static bool LocalTime(int64_t seconds_since_epoch, tm* tm_result) {
time_t seconds = static_cast<time_t>(seconds_since_epoch);
diff --git a/runtime/vm/parser.cc b/runtime/vm/parser.cc
index 59f6faa..33db8c2 100644
--- a/runtime/vm/parser.cc
+++ b/runtime/vm/parser.cc
@@ -159,22 +159,22 @@
// Compute start indices to parameters and locals, and the number of
// parameters to copy.
if (num_opt_params == 0) {
- // Parameter i will be at fp[kLastParamSlotIndex + num_params - 1 - i] and
- // local variable j will be at fp[kFirstLocalSlotIndex - j].
+ // Parameter i will be at fp[kParamEndSlotFromFp + num_params - i] and
+ // local variable j will be at fp[kFirstLocalSlotFromFp - j].
ASSERT(GetSavedArgumentsDescriptorVar() == NULL);
- first_parameter_index_ = kLastParamSlotIndex + num_params - 1;
- first_stack_local_index_ = kFirstLocalSlotIndex;
+ first_parameter_index_ = kParamEndSlotFromFp + num_params;
+ first_stack_local_index_ = kFirstLocalSlotFromFp;
num_copied_params_ = 0;
} else {
- // Parameter i will be at fp[kFirstLocalSlotIndex - i] and local variable
- // j will be at fp[kFirstLocalSlotIndex - num_params - j].
+ // Parameter i will be at fp[kFirstLocalSlotFromFp - i] and local variable
+ // j will be at fp[kFirstLocalSlotFromFp - num_params - j].
// The saved arguments descriptor variable must be allocated similarly to
// a parameter, so that it gets both a frame slot and a context slot when
// captured.
if (GetSavedArgumentsDescriptorVar() != NULL) {
num_params += 1;
}
- first_parameter_index_ = kFirstLocalSlotIndex;
+ first_parameter_index_ = kFirstLocalSlotFromFp;
first_stack_local_index_ = first_parameter_index_ - num_params;
num_copied_params_ = num_params;
}
diff --git a/runtime/vm/scopes.cc b/runtime/vm/scopes.cc
index c71fa16..c7b0c05 100644
--- a/runtime/vm/scopes.cc
+++ b/runtime/vm/scopes.cc
@@ -569,15 +569,15 @@
int LocalVariable::BitIndexIn(intptr_t fixed_parameter_count) const {
ASSERT(!is_captured());
// Parameters have positive indexes with the lowest index being
- // kLastParamSlotIndex. Locals and copied parameters have negative indexes
- // with the lowest (closest to zero) index being kFirstLocalSlotIndex.
+ // kParamEndSlotFromFp + 1. Locals and copied parameters have negative
+ // indexes with the lowest (closest to 0) index being kFirstLocalSlotFromFp.
if (index() > 0) {
// Shift non-negative indexes so that the lowest one is 0.
- return (fixed_parameter_count - 1) - (index() - kLastParamSlotIndex);
+ return fixed_parameter_count - (index() - kParamEndSlotFromFp);
} else {
// Shift negative indexes so that the lowest one is 0 (they are still
// non-positive).
- return fixed_parameter_count - (index() - kFirstLocalSlotIndex);
+ return fixed_parameter_count - (index() - kFirstLocalSlotFromFp);
}
}
diff --git a/runtime/vm/simulator_arm.cc b/runtime/vm/simulator_arm.cc
index c5d9724..59d6d29 100644
--- a/runtime/vm/simulator_arm.cc
+++ b/runtime/vm/simulator_arm.cc
@@ -1482,8 +1482,8 @@
break;
}
case 7: {
- if (instr->Bits(21, 2) == 0x1) {
- // Format(instr, "bkpt'cond #'imm12_4");
+ if ((instr->Bits(21, 2) == 0x1) && (instr->ConditionField() == AL)) {
+ // Format(instr, "bkpt #'imm12_4");
SimulatorDebugger dbg(this);
set_pc(get_pc() + Instr::kInstrSize);
char buffer[32];
@@ -2067,12 +2067,12 @@
void Simulator::DoDivision(Instr* instr) {
ASSERT(CPUFeatures::integer_division_supported());
- Register rd = instr->RdField();
- Register rn = instr->RnField();
- Register rm = instr->RmField();
+ Register rd = instr->DivRdField();
+ Register rn = instr->DivRnField();
+ Register rm = instr->DivRmField();
- // TODO(zra): Does the hardware trap on divide-by-zero?
- // Revisit when we test on ARM hardware.
+ // ARMv7-a does not trap on divide-by-zero. The destination register is just
+ // set to 0.
if (get_register(rm) == 0) {
set_register(rd, 0);
return;
diff --git a/runtime/vm/stack_frame.cc b/runtime/vm/stack_frame.cc
index 3ae8d33..b45d133 100644
--- a/runtime/vm/stack_frame.cc
+++ b/runtime/vm/stack_frame.cc
@@ -21,7 +21,7 @@
bool StackFrame::IsStubFrame() const {
ASSERT(!(IsEntryFrame() || IsExitFrame()));
uword saved_pc =
- *(reinterpret_cast<uword*>(fp() + EntrypointMarkerOffsetFromFp()));
+ *(reinterpret_cast<uword*>(fp() + (kPcMarkerSlotFromFp * kWordSize)));
return (saved_pc == 0);
}
@@ -37,17 +37,18 @@
RawContext* EntryFrame::SavedContext() const {
- return *(reinterpret_cast<RawContext**>(fp() + SavedContextOffset()));
+ return *(reinterpret_cast<RawContext**>(
+ fp() + (kSavedContextSlotFromEntryFp * kWordSize)));
}
void EntryFrame::VisitObjectPointers(ObjectPointerVisitor* visitor) {
// Visit objects between SP and (FP - callee_save_area).
ASSERT(visitor != NULL);
- RawObject** start = reinterpret_cast<RawObject**>(sp());
- RawObject** end = reinterpret_cast<RawObject**>(
- fp() - kWordSize + ExitLinkOffset());
- visitor->VisitPointers(start, end);
+ RawObject** first = reinterpret_cast<RawObject**>(sp());
+ RawObject** last = reinterpret_cast<RawObject**>(
+ fp() + (kExitLinkSlotFromEntryFp - 1) * kWordSize);
+ visitor->VisitPointers(first, last);
}
@@ -60,9 +61,9 @@
// helper functions to the raw object interface.
ASSERT(visitor != NULL);
NoGCScope no_gc;
- RawObject** start_addr = reinterpret_cast<RawObject**>(sp());
- RawObject** end_addr =
- reinterpret_cast<RawObject**>(fp()) + kFirstLocalSlotIndex;
+ RawObject** first = reinterpret_cast<RawObject**>(sp());
+ RawObject** last = reinterpret_cast<RawObject**>(
+ fp() + (kFirstLocalSlotFromFp * kWordSize));
Code code;
code = LookupDartCode();
if (!code.IsNull()) {
@@ -93,24 +94,24 @@
// Spill slots are at the 'bottom' of the frame.
intptr_t spill_slot_count = length - map.RegisterBitCount();
for (intptr_t bit = 0; bit < spill_slot_count; ++bit) {
- if (map.IsObject(bit)) visitor->VisitPointer(end_addr);
- --end_addr;
+ if (map.IsObject(bit)) visitor->VisitPointer(last);
+ --last;
}
// The live registers at the 'top' of the frame comprise the rest of the
// stack map.
for (intptr_t bit = length - 1; bit >= spill_slot_count; --bit) {
- if (map.IsObject(bit)) visitor->VisitPointer(start_addr);
- ++start_addr;
+ if (map.IsObject(bit)) visitor->VisitPointer(first);
+ ++first;
}
- // The end address can be one slot (but not more) past the start
- // address in the case that all slots were covered by the stack map.
- ASSERT((end_addr + 1) >= start_addr);
+ // The last slot can be one slot (but not more) past the last slot
+ // in the case that all slots were covered by the stack map.
+ ASSERT((last + 1) >= first);
}
}
- // Each slot between the start and end address are tagged objects.
- visitor->VisitPointers(start_addr, end_addr);
+ // Each slot between the first and last included are tagged objects.
+ visitor->VisitPointers(first, last);
}
@@ -139,11 +140,11 @@
// a GC as we are handling raw object references here. It is possible
// that the code is called while a GC is in progress, that is ok.
NoGCScope no_gc;
- uword saved_pc =
- *(reinterpret_cast<uword*>(fp() + EntrypointMarkerOffsetFromFp()));
- if (saved_pc != 0) {
- uword entry_point =
- (saved_pc - Assembler::kOffsetOfSavedPCfromEntrypoint);
+ const uword pc_marker =
+ *(reinterpret_cast<uword*>(fp() + (kPcMarkerSlotFromFp * kWordSize)));
+ if (pc_marker != 0) {
+ const uword entry_point =
+ (pc_marker - Assembler::kEntryPointToPcMarkerOffset);
RawInstructions* instr = Instructions::FromEntryPoint(entry_point);
if (instr != Instructions::null()) {
return instr->ptr()->code_;
@@ -202,6 +203,21 @@
}
+void StackFrameIterator::SetupLastExitFrameData() {
+ Isolate* current = Isolate::Current();
+ uword exit_marker = current->top_exit_frame_info();
+ frames_.fp_ = exit_marker;
+}
+
+
+void StackFrameIterator::SetupNextExitFrameData() {
+ uword exit_address = entry_.fp() + (kExitLinkSlotFromEntryFp * kWordSize);
+ uword exit_marker = *reinterpret_cast<uword*>(exit_address);
+ frames_.fp_ = exit_marker;
+ frames_.sp_ = 0;
+}
+
+
StackFrameIterator::StackFrameIterator(bool validate)
: validate_(validate), entry_(), exit_(), current_frame_(NULL) {
SetupLastExitFrameData(); // Setup data for last exit frame.
diff --git a/runtime/vm/stack_frame.h b/runtime/vm/stack_frame.h
index b7a53f1..6b527f6 100644
--- a/runtime/vm/stack_frame.h
+++ b/runtime/vm/stack_frame.h
@@ -37,16 +37,11 @@
uword sp() const { return sp_; }
uword fp() const { return fp_; }
uword pc() const {
- return *reinterpret_cast<uword*>(sp_ + PcAddressOffsetFromSp());
+ return *reinterpret_cast<uword*>(sp_ + (kSavedPcSlotFromSp * kWordSize));
}
void set_pc(uword value) {
- *reinterpret_cast<uword*>(sp_ + PcAddressOffsetFromSp()) = value;
- }
-
- void SetEntrypointMarker(uword value) {
- ASSERT(!(IsStubFrame() || IsEntryFrame() || IsExitFrame()));
- *reinterpret_cast<uword*>(fp_ + EntrypointMarkerOffsetFromFp()) = value;
+ *reinterpret_cast<uword*>(sp_ + (kSavedPcSlotFromSp * kWordSize)) = value;
}
// Visit objects in the frame.
@@ -83,11 +78,13 @@
private:
RawCode* GetCodeObject() const;
- // Target specific implementations for locating pc and caller fp/sp values.
- static intptr_t PcAddressOffsetFromSp();
- static intptr_t EntrypointMarkerOffsetFromFp();
- uword GetCallerSp() const;
- uword GetCallerFp() const;
+ uword GetCallerSp() const {
+ return fp() + (kCallerSpSlotFromFp * kWordSize);
+ }
+ uword GetCallerFp() const {
+ return *(reinterpret_cast<uword*>(
+ fp() + (kSavedCallerFpSlotFromFp * kWordSize)));
+ }
uword fp_;
uword sp_;
@@ -142,8 +139,6 @@
private:
EntryFrame() { }
- intptr_t ExitLinkOffset() const;
- intptr_t SavedContextOffset() const;
friend class StackFrameIterator;
DISALLOW_COPY_AND_ASSIGN(EntryFrame);
@@ -176,8 +171,8 @@
if (fp_ == 0) {
return false;
}
- intptr_t offset = StackFrame::PcAddressOffsetFromSp();
- uword pc = *(reinterpret_cast<uword*>(sp_ + offset));
+ const uword pc = *(reinterpret_cast<uword*>(
+ sp_ + (kSavedPcSlotFromSp * kWordSize)));
return !StubCode::InInvocationStub(pc);
}
diff --git a/runtime/vm/stack_frame_arm.cc b/runtime/vm/stack_frame_arm.cc
deleted file mode 100644
index e3ea537..0000000
--- a/runtime/vm/stack_frame_arm.cc
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright (c) 2011, 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.
-
-#include "vm/globals.h"
-#if defined(TARGET_ARCH_ARM)
-
-#include "vm/instructions.h"
-#include "vm/isolate.h"
-#include "vm/stack_frame.h"
-
-namespace dart {
-
-// The constant kExitLinkOffsetInEntryFrame must be kept in sync with the
-// code in the InvokeDartCode stub.
-static const int kSavedContextOffsetInEntryFrame = -10 * kWordSize;
-static const int kExitLinkOffsetInEntryFrame = -9 * kWordSize;
-static const int kPcAddressOffsetFromSp = -2 * kWordSize;
-static const int kEntrypointMarkerOffsetFromFp = 2 * kWordSize;
-static const int kSpOffsetFromPreviousFp = 3 * kWordSize;
-
-
-intptr_t StackFrame::PcAddressOffsetFromSp() {
- return kPcAddressOffsetFromSp;
-}
-
-
-intptr_t StackFrame::EntrypointMarkerOffsetFromFp() {
- return kEntrypointMarkerOffsetFromFp;
-}
-
-
-uword StackFrame::GetCallerSp() const {
- return fp() + kSpOffsetFromPreviousFp;
-}
-
-
-uword StackFrame::GetCallerFp() const {
- return *(reinterpret_cast<uword*>(fp()));
-}
-
-
-intptr_t EntryFrame::ExitLinkOffset() const {
- return kExitLinkOffsetInEntryFrame;
-}
-
-
-intptr_t EntryFrame::SavedContextOffset() const {
- return kSavedContextOffsetInEntryFrame;
-}
-
-
-void StackFrameIterator::SetupLastExitFrameData() {
- Isolate* current = Isolate::Current();
- uword exit_marker = current->top_exit_frame_info();
- frames_.fp_ = exit_marker;
-}
-
-
-void StackFrameIterator::SetupNextExitFrameData() {
- uword exit_address = entry_.fp() + kExitLinkOffsetInEntryFrame;
- uword exit_marker = *reinterpret_cast<uword*>(exit_address);
- frames_.fp_ = exit_marker;
- frames_.sp_ = 0;
-}
-
-} // namespace dart
-
-#endif // defined(TARGET_ARCH_ARM)
diff --git a/runtime/vm/stack_frame_arm.h b/runtime/vm/stack_frame_arm.h
index 433830f..ae669bf 100644
--- a/runtime/vm/stack_frame_arm.h
+++ b/runtime/vm/stack_frame_arm.h
@@ -9,25 +9,32 @@
/* ARM Dart Frame Layout
- | | <- TOS
-Callee frame | ... |
- | current LR | (PC of current frame)
- | PC Marker | (callee's frame code entry)
- +-------------------+
-Current frame | ... | <- SP of current frame
- | first local |
- | caller's PP |
- | caller's FP | <- FP of current frame
- | caller's LR | (PC of caller frame)
- | PC Marker | (current frame's code entry)
- +-------------------+
-Caller frame | last parameter |
- | ... |
+ | | <- TOS
+Callee frame | ... |
+ | current LR | (PC of current frame)
+ | callee's PC marker |
+ +--------------------+
+Current frame | ... | <- SP of current frame
+ | first local |
+ | caller's PP |
+ | caller's FP | <- FP of current frame
+ | caller's LR | (PC of caller frame)
+ | PC marker | (current frame's code entry + offset)
+ +--------------------+
+Caller frame | last parameter | <- SP of caller frame
+ | ... |
*/
-static const int kLastParamSlotIndex = 3; // From fp.
-static const int kFirstLocalSlotIndex = -2; // From fp.
-static const int kPcSlotIndexFromSp = -2;
+static const int kSavedPcSlotFromSp = -2;
+static const int kFirstLocalSlotFromFp = -2;
+static const int kSavedCallerFpSlotFromFp = 0;
+static const int kPcMarkerSlotFromFp = 2;
+static const int kParamEndSlotFromFp = 2; // Same slot as current pc marker.
+static const int kCallerSpSlotFromFp = 3;
+
+// Entry and exit frame layout.
+static const int kSavedContextSlotFromEntryFp = -10;
+static const int kExitLinkSlotFromEntryFp = -9;
} // namespace dart
diff --git a/runtime/vm/stack_frame_ia32.cc b/runtime/vm/stack_frame_ia32.cc
deleted file mode 100644
index ae4de47..0000000
--- a/runtime/vm/stack_frame_ia32.cc
+++ /dev/null
@@ -1,69 +0,0 @@
-// 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.
-
-#include "vm/globals.h"
-#if defined(TARGET_ARCH_IA32)
-
-#include "vm/instructions.h"
-#include "vm/isolate.h"
-#include "vm/stack_frame.h"
-
-namespace dart {
-
-// The constant kExitLinkOffsetInEntryFrame must be kept in sync with the
-// code in the InvokeDartCode stub.
-static const int kSavedContextOffsetInEntryFrame = -5 * kWordSize;
-static const int kExitLinkOffsetInEntryFrame = -4 * kWordSize;
-static const int kPcAddressOffsetFromSp = -1 * kWordSize;
-static const int kEntrypointMarkerOffsetFromFp = -1 * kWordSize;
-static const int kSpOffsetFromPreviousFp = 2 * kWordSize;
-
-
-intptr_t StackFrame::PcAddressOffsetFromSp() {
- return kPcAddressOffsetFromSp;
-}
-
-
-intptr_t StackFrame::EntrypointMarkerOffsetFromFp() {
- return kEntrypointMarkerOffsetFromFp;
-}
-
-
-uword StackFrame::GetCallerSp() const {
- return fp() + kSpOffsetFromPreviousFp;
-}
-
-
-uword StackFrame::GetCallerFp() const {
- return *(reinterpret_cast<uword*>(fp()));
-}
-
-
-intptr_t EntryFrame::ExitLinkOffset() const {
- return kExitLinkOffsetInEntryFrame;
-}
-
-
-intptr_t EntryFrame::SavedContextOffset() const {
- return kSavedContextOffsetInEntryFrame;
-}
-
-
-void StackFrameIterator::SetupLastExitFrameData() {
- Isolate* current = Isolate::Current();
- uword exit_marker = current->top_exit_frame_info();
- frames_.fp_ = exit_marker;
-}
-
-
-void StackFrameIterator::SetupNextExitFrameData() {
- uword exit_address = entry_.fp() + kExitLinkOffsetInEntryFrame;
- uword exit_marker = *reinterpret_cast<uword*>(exit_address);
- frames_.fp_ = exit_marker;
- frames_.sp_ = 0;
-}
-
-} // namespace dart
-
-#endif // defined TARGET_ARCH_IA32
diff --git a/runtime/vm/stack_frame_ia32.h b/runtime/vm/stack_frame_ia32.h
index 46bdfd5..10db2aa 100644
--- a/runtime/vm/stack_frame_ia32.h
+++ b/runtime/vm/stack_frame_ia32.h
@@ -9,23 +9,30 @@
/* IA32 Dart Frame Layout
- | | <- TOS
-Callee frame | ... |
- | current ret addr | (PC of current frame)
- +-------------------+
-Current frame | ... | <- ESP of current frame
- | first local |
- | PC Marker | (current frame's code entry)
- | caller's EBP | <- EBP of current frame
- | caller's ret addr | (PC of caller frame)
- +-------------------+
-Caller frame | last parameter |
- | ... |
+ | | <- TOS
+Callee frame | ... |
+ | current ret addr | (PC of current frame)
+ +--------------------+
+Current frame | ... | <- ESP of current frame
+ | first local |
+ | PC marker | (current frame's code entry + offset)
+ | caller's EBP | <- EBP of current frame
+ | caller's ret addr | (PC of caller frame)
+ +--------------------+
+Caller frame | last parameter | <- ESP of caller frame
+ | ... |
*/
-static const int kLastParamSlotIndex = 2; // From fp.
-static const int kFirstLocalSlotIndex = -2; // From fp.
-static const int kPcSlotIndexFromSp = -1;
+static const int kSavedPcSlotFromSp = -1;
+static const int kFirstLocalSlotFromFp = -2;
+static const int kPcMarkerSlotFromFp = -1;
+static const int kSavedCallerFpSlotFromFp = 0;
+static const int kParamEndSlotFromFp = 1; // Same slot as caller's ret addr.
+static const int kCallerSpSlotFromFp = 2;
+
+// Entry and exit frame layout.
+static const int kSavedContextSlotFromEntryFp = -5;
+static const int kExitLinkSlotFromEntryFp = -4;
} // namespace dart
diff --git a/runtime/vm/stack_frame_mips.cc b/runtime/vm/stack_frame_mips.cc
deleted file mode 100644
index d713b1c..0000000
--- a/runtime/vm/stack_frame_mips.cc
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright (c) 2013, 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.
-
-#include "vm/globals.h"
-#if defined(TARGET_ARCH_MIPS)
-
-#include "vm/stack_frame.h"
-
-namespace dart {
-
-// The constant kExitLinkOffsetInEntryFrame must be kept in sync with the
-// code in the InvokeDartCode stub.
-static const int kSavedContextOffsetInEntryFrame = -11 * kWordSize;
-static const int kExitLinkOffsetInEntryFrame = -10 * kWordSize;
-static const int kPcAddressOffsetFromSp = -2 * kWordSize;
-static const int kEntrypointMarkerOffsetFromFp = 2 * kWordSize;
-static const int kSpOffsetFromPreviousFp = 3 * kWordSize;
-
-
-intptr_t StackFrame::PcAddressOffsetFromSp() {
- return kPcAddressOffsetFromSp;
-}
-
-
-intptr_t StackFrame::EntrypointMarkerOffsetFromFp() {
- return kEntrypointMarkerOffsetFromFp;
-}
-
-
-uword StackFrame::GetCallerFp() const {
- return *(reinterpret_cast<uword*>(fp()));
-}
-
-
-uword StackFrame::GetCallerSp() const {
- return fp() + kSpOffsetFromPreviousFp;
-}
-
-
-intptr_t EntryFrame::ExitLinkOffset() const {
- return kExitLinkOffsetInEntryFrame;
-}
-
-
-intptr_t EntryFrame::SavedContextOffset() const {
- return kSavedContextOffsetInEntryFrame;
-}
-
-
-void StackFrameIterator::SetupLastExitFrameData() {
- Isolate* current = Isolate::Current();
- uword exit_marker = current->top_exit_frame_info();
- frames_.fp_ = exit_marker;
-}
-
-
-void StackFrameIterator::SetupNextExitFrameData() {
- uword exit_address = entry_.fp() + kExitLinkOffsetInEntryFrame;
- uword exit_marker = *reinterpret_cast<uword*>(exit_address);
- frames_.fp_ = exit_marker;
- frames_.sp_ = 0;
-}
-
-} // namespace dart
-
-#endif // defined TARGET_ARCH_MIPS
diff --git a/runtime/vm/stack_frame_mips.h b/runtime/vm/stack_frame_mips.h
index 8a32a38..57e387e 100644
--- a/runtime/vm/stack_frame_mips.h
+++ b/runtime/vm/stack_frame_mips.h
@@ -9,25 +9,32 @@
/* MIPS Dart Frame Layout
- | | <- TOS
-Callee frame | ... |
- | current RA | (PC of current frame)
- | PC Marker | (callee's frame code entry)
- +-------------------+
-Current frame | ... | <- SP of current frame
- | first local |
- | caller's PP |
- | caller's FP | <- FP of current frame
- | caller's RA | (PC of caller frame)
- | PC Marker | (current frame's code entry)
- +-------------------+
-Caller frame | last parameter |
- | ... |
+ | | <- TOS
+Callee frame | ... |
+ | current RA | (PC of current frame)
+ | callee's PC marker |
+ +--------------------+
+Current frame | ... | <- SP of current frame
+ | first local |
+ | caller's PP |
+ | caller's FP | <- FP of current frame
+ | caller's RA | (PC of caller frame)
+ | PC marker | (current frame's code entry + offset)
+ +--------------------+
+Caller frame | last parameter | <- SP of caller frame
+ | ... |
*/
-static const int kLastParamSlotIndex = 3; // From fp.
-static const int kFirstLocalSlotIndex = -2; // From fp.
-static const int kPcSlotIndexFromSp = -2;
+static const int kSavedPcSlotFromSp = -2;
+static const int kFirstLocalSlotFromFp = -2;
+static const int kSavedCallerFpSlotFromFp = 0;
+static const int kPcMarkerSlotFromFp = 2;
+static const int kParamEndSlotFromFp = 2; // Same slot as current pc marker.
+static const int kCallerSpSlotFromFp = 3;
+
+// Entry and exit frame layout.
+static const int kSavedContextSlotFromEntryFp = -11;
+static const int kExitLinkSlotFromEntryFp = -10;
} // namespace dart
diff --git a/runtime/vm/stack_frame_x64.cc b/runtime/vm/stack_frame_x64.cc
deleted file mode 100644
index 64ff644..0000000
--- a/runtime/vm/stack_frame_x64.cc
+++ /dev/null
@@ -1,69 +0,0 @@
-// 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.
-
-#include "vm/globals.h"
-#if defined(TARGET_ARCH_X64)
-
-#include "vm/instructions.h"
-#include "vm/isolate.h"
-#include "vm/stack_frame.h"
-
-namespace dart {
-
-// The constant kExitLinkOffsetInEntryFrame must be kept in sync with the
-// code in the InvokeDartCode stub.
-static const int kSavedContextOffsetInEntryFrame = -9 * kWordSize;
-static const int kExitLinkOffsetInEntryFrame = -8 * kWordSize;
-static const int kPcAddressOffsetFromSp = -1 * kWordSize;
-static const int kEntrypointMarkerOffsetFromFp = -1 * kWordSize;
-static const int kSpOffsetFromPreviousFp = 2 * kWordSize;
-
-
-intptr_t StackFrame::PcAddressOffsetFromSp() {
- return kPcAddressOffsetFromSp;
-}
-
-
-intptr_t StackFrame::EntrypointMarkerOffsetFromFp() {
- return kEntrypointMarkerOffsetFromFp;
-}
-
-
-uword StackFrame::GetCallerSp() const {
- return fp() + kSpOffsetFromPreviousFp;
-}
-
-
-uword StackFrame::GetCallerFp() const {
- return *(reinterpret_cast<uword*>(fp()));
-}
-
-
-intptr_t EntryFrame::ExitLinkOffset() const {
- return kExitLinkOffsetInEntryFrame;
-}
-
-
-intptr_t EntryFrame::SavedContextOffset() const {
- return kSavedContextOffsetInEntryFrame;
-}
-
-
-void StackFrameIterator::SetupLastExitFrameData() {
- Isolate* current = Isolate::Current();
- uword exit_marker = current->top_exit_frame_info();
- frames_.fp_ = exit_marker;
-}
-
-
-void StackFrameIterator::SetupNextExitFrameData() {
- uword exit_address = entry_.fp() + kExitLinkOffsetInEntryFrame;
- uword exit_marker = *reinterpret_cast<uword*>(exit_address);
- frames_.fp_ = exit_marker;
- frames_.sp_ = 0;
-}
-
-} // namespace dart
-
-#endif // defined TARGET_ARCH_X64
diff --git a/runtime/vm/stack_frame_x64.h b/runtime/vm/stack_frame_x64.h
index 084cd9e..552bd98 100644
--- a/runtime/vm/stack_frame_x64.h
+++ b/runtime/vm/stack_frame_x64.h
@@ -9,23 +9,30 @@
/* X64 Dart Frame Layout
- | | <- TOS
-Callee frame | ... |
- | current ret addr | (PC of current frame)
- +-------------------+
-Current frame | ... | <- RSP of current frame
- | first local |
- | PC Marker | (current frame's code entry)
- | caller's RBP | <- RBP of current frame
- | caller's ret addr | (PC of caller frame)
- +-------------------+
-Caller frame | last parameter |
- | ... |
+ | | <- TOS
+Callee frame | ... |
+ | current ret addr | (PC of current frame)
+ +--------------------+
+Current frame | ... | <- RSP of current frame
+ | first local |
+ | PC marker | (current frame's code entry + offset)
+ | caller's RBP | <- RBP of current frame
+ | caller's ret addr | (PC of caller frame)
+ +--------------------+
+Caller frame | last parameter | <- RSP of caller frame
+ | ... |
*/
-static const int kLastParamSlotIndex = 2; // From fp.
-static const int kFirstLocalSlotIndex = -2; // From fp.
-static const int kPcSlotIndexFromSp = -1;
+static const int kSavedPcSlotFromSp = -1;
+static const int kFirstLocalSlotFromFp = -2;
+static const int kPcMarkerSlotFromFp = -1;
+static const int kSavedCallerFpSlotFromFp = 0;
+static const int kParamEndSlotFromFp = 1; // Same slot as caller's ret addr.
+static const int kCallerSpSlotFromFp = 2;
+
+// Entry and exit frame layout.
+static const int kSavedContextSlotFromEntryFp = -9;
+static const int kExitLinkSlotFromEntryFp = -8;
} // namespace dart
diff --git a/runtime/vm/stub_code_arm.cc b/runtime/vm/stub_code_arm.cc
index 39f6497..50fecc1 100644
--- a/runtime/vm/stub_code_arm.cc
+++ b/runtime/vm/stub_code_arm.cc
@@ -241,7 +241,7 @@
// Input parameters:
// R2: smi-tagged argument count, may be zero.
-// FP[kLastParamSlotIndex]: last argument.
+// FP[kParamEndSlotFromFp + 1]: last argument.
static void PushArgumentsArray(Assembler* assembler) {
// Allocate array to store arguments of caller.
__ LoadImmediate(R1, reinterpret_cast<intptr_t>(Object::null()));
@@ -252,7 +252,7 @@
// R2: smi-tagged argument count, may be zero (was preserved by the stub).
__ Push(R0); // Array is in R0 and on top of stack.
__ add(R1, FP, ShifterOperand(R2, LSL, 1));
- __ AddImmediate(R1, (kLastParamSlotIndex - 1) * kWordSize);
+ __ AddImmediate(R1, kParamEndSlotFromFp * kWordSize);
__ AddImmediate(R3, R0, Array::data_offset() - kHeapObjectTag);
// R1: address of first argument on stack.
// R3: address of first argument in array.
@@ -279,7 +279,7 @@
// Load the receiver.
__ ldr(R2, FieldAddress(R4, ArgumentsDescriptor::count_offset()));
__ add(IP, FP, ShifterOperand(R2, LSL, 1)); // R2 is Smi.
- __ ldr(R6, Address(IP, (kLastParamSlotIndex - 1) * kWordSize));
+ __ ldr(R6, Address(IP, kParamEndSlotFromFp * kWordSize));
// Push space for the return value.
// Push the receiver.
@@ -394,11 +394,17 @@
if (preserve_result) {
__ Push(R1); // Preserve result, it will be GC-d here.
}
- __ CallRuntime(kDeoptimizeMaterializeDoublesRuntimeEntry);
+ __ PushObject(Smi::ZoneHandle()); // Space for the result.
+ __ CallRuntime(kDeoptimizeMaterializeRuntimeEntry);
+ // Result tells stub how many bytes to remove from the expression stack
+ // of the bottom-most frame. They were used as materialization arguments.
+ __ Pop(R1);
if (preserve_result) {
__ Pop(R0); // Restore result.
}
__ LeaveStubFrame();
+ // Remove materialization arguments.
+ __ add(SP, SP, ShifterOperand(R1, ASR, kSmiTagSize));
__ Ret();
}
@@ -692,16 +698,18 @@
// Save the old Context pointer. Use R4 as a temporary register.
// Note that VisitObjectPointers will find this saved Context pointer during
// GC marking, since it traverses any information between SP and
- // FP - kExitLinkOffsetInEntryFrame.
+ // FP - kExitLinkSlotFromEntryFp.
// EntryFrame::SavedContext reads the context saved in this frame.
__ LoadFromOffset(kLoadWord, R4, R8, Isolate::top_context_offset());
- // The constants kSavedContextOffsetInEntryFrame and
- // kExitLinkOffsetInEntryFrame must be kept in sync with the code below.
+ // The constants kSavedContextSlotFromEntryFp and
+ // kExitLinkSlotFromEntryFp must be kept in sync with the code below.
+ ASSERT(kExitLinkSlotFromEntryFp == -9);
+ ASSERT(kSavedContextSlotFromEntryFp == -10);
__ PushList((1 << R4) | (1 << R5));
// The stack pointer is restored after the call to this location.
- const intptr_t kSavedContextOffsetInEntryFrame = -10 * kWordSize;
+ const intptr_t kSavedContextSlotFromEntryFp = -10 * kWordSize;
// Load arguments descriptor array into R4, which is passed to Dart code.
__ ldr(R4, Address(R1, VMHandles::kOffsetOfRawPtrInHandle));
@@ -737,7 +745,7 @@
__ ldr(CTX, Address(CTX, VMHandles::kOffsetOfRawPtrInHandle));
// Get rid of arguments pushed on the stack.
- __ AddImmediate(SP, FP, kSavedContextOffsetInEntryFrame);
+ __ AddImmediate(SP, FP, kSavedContextSlotFromEntryFp);
// Load Isolate pointer from Context structure into CTX. Drop Context.
__ ldr(CTX, FieldAddress(CTX, Context::isolate_offset()));
diff --git a/runtime/vm/stub_code_ia32.cc b/runtime/vm/stub_code_ia32.cc
index 0af074b..7c22076 100644
--- a/runtime/vm/stub_code_ia32.cc
+++ b/runtime/vm/stub_code_ia32.cc
@@ -14,6 +14,7 @@
#include "vm/object_store.h"
#include "vm/resolver.h"
#include "vm/scavenger.h"
+#include "vm/stack_frame.h"
#include "vm/stub_code.h"
@@ -394,11 +395,20 @@
if (preserve_eax) {
__ pushl(EBX); // Preserve result, it will be GC-d here.
}
- __ CallRuntime(kDeoptimizeMaterializeDoublesRuntimeEntry);
+ __ pushl(Immediate(Smi::RawValue(0))); // Space for the result.
+ __ CallRuntime(kDeoptimizeMaterializeRuntimeEntry);
+ // Result tells stub how many bytes to remove from the expression stack
+ // of the bottom-most frame. They were used as materialization arguments.
+ __ popl(EBX);
+ __ SmiUntag(EBX);
if (preserve_eax) {
__ popl(EAX); // Restore result.
}
__ LeaveFrame();
+
+ __ popl(ECX); // Pop return address.
+ __ addl(ESP, EBX); // Remove materialization arguments.
+ __ pushl(ECX); // Push return address.
__ ret();
}
@@ -741,8 +751,9 @@
// Save the top exit frame info. Use EDX as a temporary register.
// StackFrameIterator reads the top exit frame info saved in this frame.
- // The constant kExitLinkOffsetInEntryFrame must be kept in sync with the
- // code below: kExitLinkOffsetInEntryFrame = -4 * kWordSize.
+ // The constant kExitLinkSlotFromEntryFp must be kept in sync with the
+ // code below.
+ ASSERT(kExitLinkSlotFromEntryFp == -4);
__ movl(EDX, Address(EDI, Isolate::top_exit_frame_info_offset()));
__ pushl(EDX);
__ movl(Address(EDI, Isolate::top_exit_frame_info_offset()), Immediate(0));
@@ -750,10 +761,11 @@
// Save the old Context pointer. Use ECX as a temporary register.
// Note that VisitObjectPointers will find this saved Context pointer during
// GC marking, since it traverses any information between SP and
- // FP - kExitLinkOffsetInEntryFrame.
+ // FP - kExitLinkSlotFromEntryFp.
// EntryFrame::SavedContext reads the context saved in this frame.
- // The constant kSavedContextOffsetInEntryFrame must be kept in sync with
- // the code below: kSavedContextOffsetInEntryFrame = -5 * kWordSize.
+ // The constant kSavedContextSlotFromEntryFp must be kept in sync with
+ // the code below.
+ ASSERT(kSavedContextSlotFromEntryFp == -5);
__ movl(ECX, Address(EDI, Isolate::top_context_offset()));
__ pushl(ECX);
diff --git a/runtime/vm/stub_code_mips.cc b/runtime/vm/stub_code_mips.cc
index 10f55d7..b85e0bc 100644
--- a/runtime/vm/stub_code_mips.cc
+++ b/runtime/vm/stub_code_mips.cc
@@ -261,7 +261,7 @@
// Input parameters:
// A1: Smi-tagged argument count, may be zero.
-// FP[kLastParamSlotIndex]: Last argument.
+// FP[kParamEndSlotFromFp + 1]: Last argument.
static void PushArgumentsArray(Assembler* assembler) {
__ TraceSimMsg("PushArgumentsArray");
// Allocate array to store arguments of caller.
@@ -275,7 +275,7 @@
__ Push(V0); // Array is in V0 and on top of stack.
__ sll(T1, A1, 1);
__ addu(T1, FP, T1);
- __ AddImmediate(T1, (kLastParamSlotIndex - 1) * kWordSize);
+ __ AddImmediate(T1, kParamEndSlotFromFp * kWordSize);
// T1: address of first argument on stack.
// T2: address of first argument in array.
@@ -308,7 +308,7 @@
__ lw(A1, FieldAddress(S4, ArgumentsDescriptor::count_offset()));
__ sll(TMP1, A1, 1); // A1 is Smi.
__ addu(TMP1, FP, TMP1);
- __ lw(T1, Address(TMP1, (kLastParamSlotIndex - 1) * kWordSize));
+ __ lw(T1, Address(TMP1, kParamEndSlotFromFp * kWordSize));
// Push space for the return value.
// Push the receiver.
@@ -451,12 +451,20 @@
if (preserve_result) {
__ Push(T1); // Preserve result, it will be GC-d here.
}
- __ CallRuntime(kDeoptimizeMaterializeDoublesRuntimeEntry);
+ __ PushObject(Smi::ZoneHandle()); // Space for the result.
+ __ CallRuntime(kDeoptimizeMaterializeRuntimeEntry);
+ // Result tells stub how many bytes to remove from the expression stack
+ // of the bottom-most frame. They were used as materialization arguments.
+ __ Pop(T1);
+ __ SmiUntag(T1);
if (preserve_result) {
__ Pop(V0); // Restore result.
}
+ __ LeaveStubFrame();
- __ LeaveStubFrameAndReturn();
+ // Return.
+ __ jr(RA);
+ __ delay_slot()->addu(SP, SP, T1); // Remove materialization arguments.
}
@@ -773,18 +781,19 @@
// Save the old Context pointer. Use T1 as a temporary register.
// Note that VisitObjectPointers will find this saved Context pointer during
// GC marking, since it traverses any information between SP and
- // FP - kExitLinkOffsetInEntryFrame.
+ // FP - kExitLinkSlotFromEntryFp.
// EntryFrame::SavedContext reads the context saved in this frame.
__ lw(T1, Address(T2, Isolate::top_context_offset()));
- // The constants kSavedContextOffsetInEntryFrame and
- // kExitLinkOffsetInEntryFrame must be kept in sync with the code below.
+ // The constants kSavedContextSlotFromEntryFp and
+ // kExitLinkSlotFromEntryFp must be kept in sync with the code below.
+ ASSERT(kExitLinkSlotFromEntryFp == -10);
+ ASSERT(kSavedContextSlotFromEntryFp == -11);
__ sw(T0, Address(SP, 1 * kWordSize));
__ sw(T1, Address(SP, 0 * kWordSize));
// After the call, The stack pointer is restored to this location.
// Pushed A3, S0-7, T0, T1 = 11.
- const intptr_t kSavedContextOffsetInEntryFrame = -11 * kWordSize;
// Load arguments descriptor array into S4, which is passed to Dart code.
__ lw(S4, Address(A1, VMHandles::kOffsetOfRawPtrInHandle));
@@ -824,7 +833,7 @@
__ lw(CTX, Address(CTX, VMHandles::kOffsetOfRawPtrInHandle));
// Get rid of arguments pushed on the stack.
- __ AddImmediate(SP, FP, kSavedContextOffsetInEntryFrame);
+ __ AddImmediate(SP, FP, kSavedContextSlotFromEntryFp * kWordSize);
// Load Isolate pointer from Context structure into CTX. Drop Context.
__ lw(CTX, FieldAddress(CTX, Context::isolate_offset()));
diff --git a/runtime/vm/stub_code_x64.cc b/runtime/vm/stub_code_x64.cc
index 1654efb..3db3c7e 100644
--- a/runtime/vm/stub_code_x64.cc
+++ b/runtime/vm/stub_code_x64.cc
@@ -14,6 +14,7 @@
#include "vm/object_store.h"
#include "vm/resolver.h"
#include "vm/scavenger.h"
+#include "vm/stack_frame.h"
#include "vm/stub_code.h"
@@ -387,12 +388,20 @@
if (preserve_rax) {
__ pushq(RBX); // Preserve result, it will be GC-d here.
}
- __ CallRuntime(kDeoptimizeMaterializeDoublesRuntimeEntry);
+ __ pushq(Immediate(Smi::RawValue(0))); // Space for the result.
+ __ CallRuntime(kDeoptimizeMaterializeRuntimeEntry);
+ // Result tells stub how many bytes to remove from the expression stack
+ // of the bottom-most frame. They were used as materialization arguments.
+ __ popq(RBX);
+ __ SmiUntag(RBX);
if (preserve_rax) {
__ popq(RAX); // Restore result.
}
__ LeaveFrame();
+ __ popq(RCX); // Pop return address.
+ __ addq(RSP, RBX); // Remove materialization arguments.
+ __ pushq(RCX); // Push return address.
__ ret();
}
@@ -729,8 +738,9 @@
// Save the top exit frame info. Use RAX as a temporary register.
// StackFrameIterator reads the top exit frame info saved in this frame.
- // The constant kExitLinkOffsetInEntryFrame must be kept in sync with the
- // code below: kExitLinkOffsetInEntryFrame = -8 * kWordSize.
+ // The constant kExitLinkSlotFromEntryFp must be kept in sync with the
+ // code below.
+ ASSERT(kExitLinkSlotFromEntryFp == -8);
__ movq(RAX, Address(R8, Isolate::top_exit_frame_info_offset()));
__ pushq(RAX);
__ movq(Address(R8, Isolate::top_exit_frame_info_offset()), Immediate(0));
@@ -738,10 +748,11 @@
// Save the old Context pointer. Use RAX as a temporary register.
// Note that VisitObjectPointers will find this saved Context pointer during
// GC marking, since it traverses any information between SP and
- // FP - kExitLinkOffsetInEntryFrame.
+ // FP - kExitLinkSlotFromEntryFp * kWordSize.
// EntryFrame::SavedContext reads the context saved in this frame.
- // The constant kSavedContextOffsetInEntryFrame must be kept in sync with
- // the code below: kSavedContextOffsetInEntryFrame = -9 * kWordSize.
+ // The constant kSavedContextSlotFromEntryFp must be kept in sync with
+ // the code below.
+ ASSERT(kSavedContextSlotFromEntryFp == -9);
__ movq(RAX, Address(R8, Isolate::top_context_offset()));
__ pushq(RAX);
diff --git a/runtime/vm/version.h b/runtime/vm/version.h
index dee350b..bcc0876 100644
--- a/runtime/vm/version.h
+++ b/runtime/vm/version.h
@@ -11,7 +11,7 @@
class Version : public AllStatic {
public:
- static const char* String() { return str_; }
+ static const char* String();
private:
static const char* str_;
diff --git a/runtime/vm/version_in.cc b/runtime/vm/version_in.cc
index d479d9f..5e40516 100644
--- a/runtime/vm/version_in.cc
+++ b/runtime/vm/version_in.cc
@@ -4,8 +4,28 @@
#include "vm/version.h"
+#include "vm/cpu.h"
+#include "vm/os.h"
+
namespace dart {
+// TODO(iposva): Avoid racy initialization.
+static const char* formatted_version = NULL;
+
+const char* Version::String() {
+ if (formatted_version == NULL) {
+ const char* format = "%s on \"%s_%s\"";
+ const char* os = OS::Name();
+ const char* arch = CPU::Id();
+
+ intptr_t len = OS::SNPrint(NULL, 0, format, str_, os, arch);
+ char* buffer = reinterpret_cast<char*>(malloc(len + 1));
+ OS::SNPrint(buffer, (len + 1), format, str_, os, arch);
+ formatted_version = buffer;
+ }
+ return formatted_version;
+}
+
const char* Version::str_ = "{{VERSION_STR}} ({{BUILD_TIME}})";
} // namespace dart
diff --git a/runtime/vm/vm.gypi b/runtime/vm/vm.gypi
index a9fd7c2..9e073d1 100644
--- a/runtime/vm/vm.gypi
+++ b/runtime/vm/vm.gypi
@@ -274,8 +274,8 @@
{
'action_name': 'generate_corelib_patch_cc',
'inputs': [
- '../tools/create_string_literal.py',
- '<(builtin_in_cc_file)',
+ '../tools/gen_library_src_paths.py',
+ '<(libgen_in_cc_file)',
'<@(_sources)',
],
'outputs': [
@@ -283,11 +283,12 @@
],
'action': [
'python',
- 'tools/create_string_literal.py',
+ 'tools/gen_library_src_paths.py',
'--output', '<(corelib_patch_cc_file)',
- '--input_cc', '<(builtin_in_cc_file)',
+ '--input_cc', '<(libgen_in_cc_file)',
'--include', 'vm/bootstrap.h',
- '--var_name', 'dart::Bootstrap::corelib_patch_',
+ '--var_name', 'dart::Bootstrap::corelib_patch_paths_',
+ '--library_name', 'dart:corelib',
'<@(_sources)',
],
'message': 'Generating ''<(corelib_patch_cc_file)'' file.'
@@ -353,8 +354,8 @@
{
'action_name': 'generate_collection_dev_patch_cc',
'inputs': [
- '../tools/create_string_literal.py',
- '<(builtin_in_cc_file)',
+ '../tools/gen_library_src_paths.py',
+ '<(libgen_in_cc_file)',
'<@(_sources)',
],
'outputs': [
@@ -362,11 +363,12 @@
],
'action': [
'python',
- 'tools/create_string_literal.py',
+ 'tools/gen_library_src_paths.py',
'--output', '<(collection_dev_patch_cc_file)',
- '--input_cc', '<(builtin_in_cc_file)',
+ '--input_cc', '<(libgen_in_cc_file)',
'--include', 'vm/bootstrap.h',
- '--var_name', 'dart::Bootstrap::collection_dev_patch_',
+ '--var_name', 'dart::Bootstrap::collection_dev_patch_paths_',
+ '--library_name', 'dart:_collection-dev',
'<@(_sources)',
],
'message': 'Generating ''<(collection_dev_patch_cc_file)'' file.'
@@ -505,8 +507,8 @@
{
'action_name': 'generate_math_patch_cc',
'inputs': [
- '../tools/create_string_literal.py',
- '<(builtin_in_cc_file)',
+ '../tools/gen_library_src_paths.py',
+ '<(libgen_in_cc_file)',
'<@(_sources)',
],
'outputs': [
@@ -514,11 +516,12 @@
],
'action': [
'python',
- 'tools/create_string_literal.py',
+ 'tools/gen_library_src_paths.py',
'--output', '<(math_patch_cc_file)',
- '--input_cc', '<(builtin_in_cc_file)',
+ '--input_cc', '<(libgen_in_cc_file)',
'--include', 'vm/bootstrap.h',
- '--var_name', 'dart::Bootstrap::math_patch_',
+ '--var_name', 'dart::Bootstrap::math_patch_paths_',
+ '--library_name', 'dart:math',
'<@(_sources)',
],
'message': 'Generating ''<(math_patch_cc_file)'' file.'
@@ -584,8 +587,8 @@
{
'action_name': 'generate_mirrors_patch_cc',
'inputs': [
- '../tools/create_string_literal.py',
- '<(builtin_in_cc_file)',
+ '../tools/gen_library_src_paths.py',
+ '<(libgen_in_cc_file)',
'<@(_sources)',
],
'outputs': [
@@ -593,11 +596,12 @@
],
'action': [
'python',
- 'tools/create_string_literal.py',
+ 'tools/gen_library_src_paths.py',
'--output', '<(mirrors_patch_cc_file)',
- '--input_cc', '<(builtin_in_cc_file)',
+ '--input_cc', '<(libgen_in_cc_file)',
'--include', 'vm/bootstrap.h',
- '--var_name', 'dart::Bootstrap::mirrors_patch_',
+ '--var_name', 'dart::Bootstrap::mirrors_patch_paths_',
+ '--library_name', 'dart:mirrors',
'<@(_sources)',
],
'message': 'Generating ''<(mirrors_patch_cc_file)'' file.'
@@ -663,8 +667,8 @@
{
'action_name': 'generate_async_patch_cc',
'inputs': [
- '../tools/create_string_literal.py',
- '<(builtin_in_cc_file)',
+ '../tools/gen_library_src_paths.py',
+ '<(libgen_in_cc_file)',
'<@(_sources)',
],
'outputs': [
@@ -672,11 +676,12 @@
],
'action': [
'python',
- 'tools/create_string_literal.py',
+ 'tools/gen_library_src_paths.py',
'--output', '<(async_patch_cc_file)',
- '--input_cc', '<(builtin_in_cc_file)',
+ '--input_cc', '<(libgen_in_cc_file)',
'--include', 'vm/bootstrap.h',
- '--var_name', 'dart::Bootstrap::async_patch_',
+ '--var_name', 'dart::Bootstrap::async_patch_paths_',
+ '--library_name', 'dart:async',
'<@(_sources)',
],
'message': 'Generating ''<(async_patch_cc_file)'' file.'
@@ -702,8 +707,8 @@
{
'action_name': 'generate_collection_patch_cc',
'inputs': [
- '../tools/create_string_literal.py',
- '<(builtin_in_cc_file)',
+ '../tools/gen_library_src_paths.py',
+ '<(libgen_in_cc_file)',
'<@(_sources)',
],
'outputs': [
@@ -711,11 +716,12 @@
],
'action': [
'python',
- 'tools/create_string_literal.py',
+ 'tools/gen_library_src_paths.py',
'--output', '<(collection_patch_cc_file)',
- '--input_cc', '<(builtin_in_cc_file)',
+ '--input_cc', '<(libgen_in_cc_file)',
'--include', 'vm/bootstrap.h',
- '--var_name', 'dart::Bootstrap::collection_patch_',
+ '--var_name', 'dart::Bootstrap::collection_patch_paths_',
+ '--library_name', 'dart:collection',
'<@(_sources)',
],
'message': 'Generating ''<(collection_patch_cc_file)'' file.'
@@ -741,8 +747,8 @@
{
'action_name': 'generate_isolate_patch_cc',
'inputs': [
- '../tools/create_string_literal.py',
- '<(builtin_in_cc_file)',
+ '../tools/gen_library_src_paths.py',
+ '<(libgen_in_cc_file)',
'<@(_sources)',
],
'outputs': [
@@ -750,11 +756,12 @@
],
'action': [
'python',
- 'tools/create_string_literal.py',
+ 'tools/gen_library_src_paths.py',
'--output', '<(isolate_patch_cc_file)',
- '--input_cc', '<(builtin_in_cc_file)',
+ '--input_cc', '<(libgen_in_cc_file)',
'--include', 'vm/bootstrap.h',
- '--var_name', 'dart::Bootstrap::isolate_patch_',
+ '--var_name', 'dart::Bootstrap::isolate_patch_paths_',
+ '--library_name', 'dart:isolate',
'<@(_sources)',
],
'message': 'Generating ''<(isolate_patch_cc_file)'' file.'
@@ -813,8 +820,8 @@
{
'action_name': 'generate_json_patch_cc',
'inputs': [
- '../tools/create_string_literal.py',
- '<(builtin_in_cc_file)',
+ '../tools/gen_library_src_paths.py',
+ '<(libgen_in_cc_file)',
'<@(_sources)',
],
'outputs': [
@@ -822,11 +829,12 @@
],
'action': [
'python',
- 'tools/create_string_literal.py',
+ 'tools/gen_library_src_paths.py',
'--output', '<(json_patch_cc_file)',
- '--input_cc', '<(builtin_in_cc_file)',
+ '--input_cc', '<(libgen_in_cc_file)',
'--include', 'vm/bootstrap.h',
- '--var_name', 'dart::Bootstrap::json_patch_',
+ '--var_name', 'dart::Bootstrap::json_patch_paths_',
+ '--library_name', 'dart:json',
'<@(_sources)',
],
'message': 'Generating ''<(json_patch_cc_file)'' file.'
@@ -892,8 +900,8 @@
{
'action_name': 'generate_typed_data_patch_cc',
'inputs': [
- '../tools/create_string_literal.py',
- '<(builtin_in_cc_file)',
+ '../tools/gen_library_src_paths.py',
+ '<(libgen_in_cc_file)',
'<@(_sources)',
],
'outputs': [
@@ -901,11 +909,12 @@
],
'action': [
'python',
- 'tools/create_string_literal.py',
+ 'tools/gen_library_src_paths.py',
'--output', '<(typed_data_patch_cc_file)',
- '--input_cc', '<(builtin_in_cc_file)',
+ '--input_cc', '<(libgen_in_cc_file)',
'--include', 'vm/bootstrap.h',
- '--var_name', 'dart::Bootstrap::typed_data_patch_',
+ '--var_name', 'dart::Bootstrap::typed_data_patch_paths_',
+ '--library_name', 'dart:typed_data',
'<@(_sources)',
],
'message': 'Generating ''<(typed_data_patch_cc_file)'' file.'
diff --git a/runtime/vm/vm_sources.gypi b/runtime/vm/vm_sources.gypi
index 48f3892..6c68fba 100644
--- a/runtime/vm/vm_sources.gypi
+++ b/runtime/vm/vm_sources.gypi
@@ -286,14 +286,10 @@
'snapshot_test.cc',
'stack_frame.cc',
'stack_frame.h',
- 'stack_frame_arm.cc',
'stack_frame_arm.h',
- 'stack_frame_ia32.cc',
'stack_frame_ia32.h',
- 'stack_frame_mips.cc',
'stack_frame_mips.h',
'stack_frame_test.cc',
- 'stack_frame_x64.cc',
'stack_frame_x64.h',
'store_buffer.cc',
'store_buffer.h',
diff --git a/sdk/lib/_internal/compiler/implementation/closure.dart b/sdk/lib/_internal/compiler/implementation/closure.dart
index 48fa3b3..b9b83e2 100644
--- a/sdk/lib/_internal/compiler/implementation/closure.dart
+++ b/sdk/lib/_internal/compiler/implementation/closure.dart
@@ -585,22 +585,25 @@
Link<String> parts = const Link<String>();
SourceString ownName = element.name;
if (ownName == null || ownName.stringValue == "") {
- parts = parts.prepend("anon");
+ parts = parts.prepend("closure");
} else {
parts = parts.prepend(ownName.slowToString());
}
for (Element enclosingElement = element.enclosingElement;
enclosingElement != null &&
- (identical(enclosingElement.kind,
- ElementKind.GENERATIVE_CONSTRUCTOR_BODY)
- || identical(enclosingElement.kind, ElementKind.CLASS)
- || identical(enclosingElement.kind, ElementKind.FUNCTION)
- || identical(enclosingElement.kind, ElementKind.GETTER)
- || identical(enclosingElement.kind, ElementKind.SETTER));
+ (enclosingElement.kind == ElementKind.GENERATIVE_CONSTRUCTOR_BODY
+ || enclosingElement.kind == ElementKind.GENERATIVE_CONSTRUCTOR
+ || enclosingElement.kind == ElementKind.CLASS
+ || enclosingElement.kind == ElementKind.FUNCTION
+ || enclosingElement.kind == ElementKind.GETTER
+ || enclosingElement.kind == ElementKind.SETTER);
enclosingElement = enclosingElement.enclosingElement) {
SourceString surroundingName =
Elements.operatorNameToIdentifier(enclosingElement.name);
parts = parts.prepend(surroundingName.slowToString());
+ // A generative constructors's parent is the class; the class name is
+ // already part of the generative constructor's name.
+ if (enclosingElement.kind == ElementKind.GENERATIVE_CONSTRUCTOR) break;
}
StringBuffer sb = new StringBuffer();
parts.printOn(sb, '_');
diff --git a/sdk/lib/_internal/compiler/implementation/elements/modelx.dart b/sdk/lib/_internal/compiler/implementation/elements/modelx.dart
index 4253cb1..dffa694 100644
--- a/sdk/lib/_internal/compiler/implementation/elements/modelx.dart
+++ b/sdk/lib/_internal/compiler/implementation/elements/modelx.dart
@@ -387,14 +387,18 @@
}
void add(Element element, DiagnosticListener listener) {
+ SourceString name = element.name;
if (element.isAccessor()) {
- addAccessor(element, contents[element.name], listener);
+ addAccessor(element, contents[name], listener);
} else {
- Element existing = contents.putIfAbsent(element.name, () => element);
+ Element existing = contents.putIfAbsent(name, () => element);
if (!identical(existing, element)) {
- // TODO(ahe): Do something similar to Resolver.reportErrorWithContext.
- listener.cancel('duplicate definition', token: element.position());
- listener.cancel('existing definition', token: existing.position());
+ listener.reportErrorCode(element,
+ MessageKind.DUPLICATE_DEFINITION, {'name': name});
+ listener.reportMessage(
+ listener.spanFromSpannable(existing),
+ MessageKind.EXISTING_DEFINITION.error({'name': name}),
+ api.Diagnostic.INFO);
}
}
}
diff --git a/sdk/lib/_internal/compiler/implementation/lib/isolate_helper.dart b/sdk/lib/_internal/compiler/implementation/lib/isolate_helper.dart
index db10b54..cf639f2 100644
--- a/sdk/lib/_internal/compiler/implementation/lib/isolate_helper.dart
+++ b/sdk/lib/_internal/compiler/implementation/lib/isolate_helper.dart
@@ -614,7 +614,7 @@
// TODO(sigmund): clean up above, after we make the new API the default:
static spawn(String functionName, String uri, bool isLight) {
- Completer<SendPort> completer = new Completer<SendPort>();
+ Completer<SendPort> completer = new Completer.sync<SendPort>();
ReceivePort port = new ReceivePort();
port.receive((msg, SendPort replyPort) {
port.close();
diff --git a/sdk/lib/_internal/compiler/implementation/resolution/members.dart b/sdk/lib/_internal/compiler/implementation/resolution/members.dart
index 49cd0f9..7e1beaa 100644
--- a/sdk/lib/_internal/compiler/implementation/resolution/members.dart
+++ b/sdk/lib/_internal/compiler/implementation/resolution/members.dart
@@ -1757,7 +1757,12 @@
if (doAddToScope) {
Element existing = scope.add(element);
if (existing != element) {
- error(node, MessageKind.DUPLICATE_DEFINITION, {'name': node});
+ compiler.reportErrorCode(
+ node, MessageKind.DUPLICATE_DEFINITION, {'name': node});
+ compiler.reportMessage(
+ compiler.spanFromSpannable(existing),
+ MessageKind.EXISTING_DEFINITION.error({'name': node}),
+ Diagnostic.INFO);
}
}
return element;
@@ -2130,18 +2135,25 @@
void resolveArguments(NodeList list) {
if (list == null) return;
- List<SourceString> seenNamedArguments = <SourceString>[];
+ Map<SourceString, Node> seenNamedArguments = new Map<SourceString, Node>();
for (Link<Node> link = list.nodes; !link.isEmpty; link = link.tail) {
Expression argument = link.head;
visit(argument);
NamedArgument namedArgument = argument.asNamedArgument();
if (namedArgument != null) {
SourceString source = namedArgument.name.source;
- if (seenNamedArguments.contains(source)) {
- error(argument, MessageKind.DUPLICATE_DEFINITION,
- {'name': source});
+ if (seenNamedArguments.containsKey(source)) {
+ compiler.reportErrorCode(
+ argument,
+ MessageKind.DUPLICATE_DEFINITION,
+ {'name': source});
+ compiler.reportMessage(
+ compiler.spanFromSpannable(seenNamedArguments[source]),
+ MessageKind.EXISTING_DEFINITION.error({'name': source}),
+ Diagnostic.INFO);
+ } else {
+ seenNamedArguments[source] = namedArgument;
}
- seenNamedArguments.add(source);
} else if (!seenNamedArguments.isEmpty) {
error(argument, MessageKind.INVALID_ARGUMENT_AFTER_NAMED);
}
diff --git a/sdk/lib/_internal/compiler/implementation/warnings.dart b/sdk/lib/_internal/compiler/implementation/warnings.dart
index 00cdcce..189c326 100644
--- a/sdk/lib/_internal/compiler/implementation/warnings.dart
+++ b/sdk/lib/_internal/compiler/implementation/warnings.dart
@@ -55,7 +55,9 @@
static const CANNOT_RESOLVE_TYPE = const MessageKind(
'cannot resolve type #{typeName}');
static const DUPLICATE_DEFINITION = const MessageKind(
- 'duplicate definition of #{name}');
+ "Error: Duplicate definition of '#{name}'.");
+ static const EXISTING_DEFINITION = const MessageKind(
+ "Info: Existing definition of '#{name}'.");
static const DUPLICATE_IMPORT = const MessageKind(
'duplicate import of #{name}');
static const DUPLICATE_EXPORT = const MessageKind(
diff --git a/sdk/lib/_internal/pub/bin/pub.dart b/sdk/lib/_internal/pub/bin/pub.dart
index 0d6e72a..f3b8674 100644
--- a/sdk/lib/_internal/pub/bin/pub.dart
+++ b/sdk/lib/_internal/pub/bin/pub.dart
@@ -37,7 +37,7 @@
'all': 'Show all output including internal tracing messages.'
});
parser.addFlag('verbose', abbr: 'v', negatable: false,
- help: 'Shortcut for "--verbosity=all"');
+ help: 'Shortcut for "--verbosity=all".');
return parser;
}
@@ -91,8 +91,6 @@
}
validatePlatform().then((_) {
- var cache = new SystemCache.withSources(cacheDir);
-
// Select the command.
var command = PubCommand.commands[globalOptions.rest[0]];
if (command == null) {
@@ -103,7 +101,7 @@
}
var commandArgs = globalOptions.rest.sublist(1);
- command.run(cache, globalOptions, commandArgs);
+ command.run(cacheDir, globalOptions, commandArgs);
});
}
diff --git a/sdk/lib/_internal/pub/lib/src/command.dart b/sdk/lib/_internal/pub/lib/src/command.dart
index 21ad9b6..fb0a842 100644
--- a/sdk/lib/_internal/pub/lib/src/command.dart
+++ b/sdk/lib/_internal/pub/lib/src/command.dart
@@ -69,9 +69,13 @@
/// available in [commandOptions].
ArgParser get commandParser => new ArgParser();
- void run(SystemCache cache_, ArgResults globalOptions_,
+ /// Override this to use offline-only sources instead of hitting the network.
+ /// This will only be called before the [SystemCache] is created. After that,
+ /// it has no effect.
+ bool get isOffline => false;
+
+ void run(String cacheDir, ArgResults globalOptions_,
List<String> commandArgs) {
- cache = cache_;
globalOptions = globalOptions_;
try {
@@ -82,6 +86,8 @@
exit(exit_codes.USAGE);
}
+ cache = new SystemCache.withSources(cacheDir, isOffline: isOffline);
+
handleError(error) {
var trace = getAttachedStackTrace(error);
@@ -139,7 +145,7 @@
if (commandFuture == null) return true;
return commandFuture;
- }).whenComplete(() => cache_.deleteTempDir()).catchError((e) {
+ }).whenComplete(() => cache.deleteTempDir()).catchError((e) {
if (e is PubspecNotFoundException && e.name == null) {
e = 'Could not find a file named "pubspec.yaml" in the directory '
'${path.current}.';
diff --git a/sdk/lib/_internal/pub/lib/src/command_install.dart b/sdk/lib/_internal/pub/lib/src/command_install.dart
index d51b553..a57886f 100644
--- a/sdk/lib/_internal/pub/lib/src/command_install.dart
+++ b/sdk/lib/_internal/pub/lib/src/command_install.dart
@@ -6,6 +6,8 @@
import 'dart:async';
+import 'package:args/args.dart';
+
import 'command.dart';
import 'entrypoint.dart';
import 'log.dart' as log;
@@ -15,6 +17,14 @@
String get description => "Install the current package's dependencies.";
String get usage => "pub install";
+ ArgParser get commandParser {
+ return new ArgParser()
+ ..addFlag('offline',
+ help: 'Use cached packages instead of accessing the network.');
+ }
+
+ bool get isOffline => commandOptions['offline'];
+
Future onRun() {
return entrypoint.installDependencies()
.then((_) => log.message("Dependencies installed!"));
diff --git a/sdk/lib/_internal/pub/lib/src/command_lish.dart b/sdk/lib/_internal/pub/lib/src/command_lish.dart
index cc03118..44a13ae 100644
--- a/sdk/lib/_internal/pub/lib/src/command_lish.dart
+++ b/sdk/lib/_internal/pub/lib/src/command_lish.dart
@@ -35,11 +35,11 @@
// TODO(nweiz): Use HostedSource.defaultUrl as the default value once we use
// dart:io for HTTPS requests.
parser.addFlag('dry-run', abbr: 'n', negatable: false,
- help: 'Validate but do not publish the package');
+ help: 'Validate but do not publish the package.');
parser.addFlag('force', abbr: 'f', negatable: false,
- help: 'Publish without confirmation if there are no errors');
+ help: 'Publish without confirmation if there are no errors.');
parser.addOption('server', defaultsTo: 'https://pub.dartlang.org',
- help: 'The package server to which to upload this package');
+ help: 'The package server to which to upload this package.');
return parser;
}
diff --git a/sdk/lib/_internal/pub/lib/src/command_update.dart b/sdk/lib/_internal/pub/lib/src/command_update.dart
index 147e925..707ac6f 100644
--- a/sdk/lib/_internal/pub/lib/src/command_update.dart
+++ b/sdk/lib/_internal/pub/lib/src/command_update.dart
@@ -6,6 +6,8 @@
import 'dart:async';
+import 'package:args/args.dart';
+
import 'command.dart';
import 'entrypoint.dart';
import 'log.dart' as log;
@@ -17,6 +19,14 @@
String get usage => 'pub update [dependencies...]';
+ ArgParser get commandParser {
+ return new ArgParser()
+ ..addFlag('offline',
+ help: 'Use cached packages instead of accessing the network.');
+ }
+
+ bool get isOffline => commandOptions['offline'];
+
Future onRun() {
var future;
if (commandOptions.rest.isEmpty) {
@@ -24,7 +34,13 @@
} else {
future = entrypoint.updateDependencies(commandOptions.rest);
}
- return future
- .then((_) => log.message("Dependencies updated!"));
+
+ return future.then((_) {
+ log.message("Dependencies updated!");
+ if (isOffline) {
+ log.warning("Warning: Updating when offline may not update you to the "
+ "latest versions of your dependencies.");
+ }
+ });
}
}
diff --git a/sdk/lib/_internal/pub/lib/src/command_uploader.dart b/sdk/lib/_internal/pub/lib/src/command_uploader.dart
index 958f298..4097fbd 100644
--- a/sdk/lib/_internal/pub/lib/src/command_uploader.dart
+++ b/sdk/lib/_internal/pub/lib/src/command_uploader.dart
@@ -31,9 +31,9 @@
// TODO(nweiz): Use HostedSource.defaultUrl as the default value once we use
// dart:io for HTTPS requests.
parser.addOption('server', defaultsTo: 'https://pub.dartlang.org',
- help: 'The package server on which the package is hosted');
+ help: 'The package server on which the package is hosted.');
parser.addOption('package', help: 'The package whose uploaders will be '
- 'modified\n'
+ 'modified.\n'
'(defaults to the current package)');
return parser;
}
diff --git a/sdk/lib/_internal/pub/lib/src/entrypoint.dart b/sdk/lib/_internal/pub/lib/src/entrypoint.dart
index e8fa70e..9341c9b 100644
--- a/sdk/lib/_internal/pub/lib/src/entrypoint.dart
+++ b/sdk/lib/_internal/pub/lib/src/entrypoint.dart
@@ -171,18 +171,16 @@
isSelfLink: true, relative: true);
}
- /// If `bin/`, `test/`, or `example/` directories exist, symlink `packages/`
- /// into them so that their entrypoints can be run. Do the same for any
- /// subdirectories of `test/` and `example/`.
+ /// Add "packages" directories to the whitelist of directories that may
+ /// contain Dart entrypoints.
void _linkSecondaryPackageDirs() {
+ // Only the main "bin" directory gets a "packages" directory, not its
+ // subdirectories.
var binDir = path.join(root.dir, 'bin');
- var exampleDir = path.join(root.dir, 'example');
- var testDir = path.join(root.dir, 'test');
- var toolDir = path.join(root.dir, 'tool');
- var webDir = path.join(root.dir, 'web');
-
if (dirExists(binDir)) _linkSecondaryPackageDir(binDir);
- for (var dir in ['example', 'test', 'tool', 'web']) {
+
+ // The others get "packages" directories in subdirectories too.
+ for (var dir in ['benchmark', 'example', 'test', 'tool', 'web']) {
_linkSecondaryPackageDirsRecursively(path.join(root.dir, dir));
}
}
diff --git a/sdk/lib/_internal/pub/lib/src/hosted_source.dart b/sdk/lib/_internal/pub/lib/src/hosted_source.dart
index 1b4ecda..17b1839 100644
--- a/sdk/lib/_internal/pub/lib/src/hosted_source.dart
+++ b/sdk/lib/_internal/pub/lib/src/hosted_source.dart
@@ -98,13 +98,10 @@
/// from that site.
Future<String> systemCacheDirectory(PackageId id) {
var parsed = _parseDescription(id.description);
- var url = _getSourceDirectory(parsed.last);
- var urlDir = replace(url, new RegExp(r'[<>:"\\/|?*%]'), (match) {
- return '%${match[0].codeUnitAt(0)}';
- });
+ var dir = _getSourceDirectory(parsed.last);
return new Future.value(
- path.join(systemCacheRoot, urlDir, "${parsed.first}-${id.version}"));
+ path.join(systemCacheRoot, dir, "${parsed.first}-${id.version}"));
}
String packageName(description) => _parseDescription(description).first;
@@ -123,19 +120,21 @@
return description;
}
- List<Package> getCachedPackages() {
+ List<Package> getCachedPackages([String url]) {
+ if (url == null) url = _defaultUrl;
+
var cacheDir = path.join(systemCacheRoot,
- _getSourceDirectory(_defaultUrl));
+ _getSourceDirectory(url));
if (!dirExists(cacheDir)) return [];
return listDir(path.join(cacheDir)).map((entry) {
// TODO(keertip): instead of catching exception in pubspec parse with
- // sdk dependency, fix to parse and report usage of sdk dependency.
+ // sdk dependency, fix to parse and report usage of sdk dependency.
// dartbug.com/10190
try {
return new Package.load(null, entry, systemCache.sources);
} on ArgumentError catch (e) {
- log.error(e);
+ log.error(e);
}
}).where((package) => package != null).toList();
}
@@ -161,14 +160,52 @@
// Otherwise re-throw the original exception.
throw error;
}
+}
+/// This is the modified hosted source used when pub install or update are run
+/// with "--offline". This uses the system cache to get the list of available
+/// packages and does no network access.
+class OfflineHostedSource extends HostedSource {
+ /// Gets the list of all versions of [name] that are in the system cache.
+ Future<List<Version>> getVersions(String name, description) {
+ return new Future(() {
+ var parsed = _parseDescription(description);
+ var server = parsed.last;
+ log.io("Finding versions of $name in "
+ "${systemCache.rootDir}/${_getSourceDirectory(server)}");
+ return getCachedPackages(server)
+ .where((package) => package.name == name)
+ .map((package) => package.version)
+ .toList();
+ }).then((versions) {
+ // If there are no versions in the cache, report a clearer error.
+ if (versions.isEmpty) fail('Could not find package "$name" in cache.');
+
+ return versions;
+ });
+ }
+
+ Future<bool> install(PackageId id, String destPath) {
+ // Since HostedSource returns `true` for [shouldCache], install will only
+ // be called for uncached packages.
+ throw new UnsupportedError("Cannot install packages when offline.");
+ }
+
+ Future<Pubspec> describe(PackageId id) {
+ // [getVersions()] will only return packages that are already cached.
+ // SystemCache should only call [describe()] on a package after it has
+ // failed to find it in the cache, so this code should not be reached.
+ throw new UnsupportedError("Cannot describe packages when offline.");
+ }
}
/// The URL of the default package repository.
final _defaultUrl = "https://pub.dartlang.org";
String _getSourceDirectory(String url) {
- return url.replaceAll(new RegExp(r"^https?://"), "");
+ url = url.replaceAll(new RegExp(r"^https?://"), "");
+ return replace(url, new RegExp(r'[<>:"\\/|?*%]'),
+ (match) => '%${match[0].codeUnitAt(0)}');
}
/// Parses [description] into its server and package name components, then
diff --git a/sdk/lib/_internal/pub/lib/src/io.dart b/sdk/lib/_internal/pub/lib/src/io.dart
index 1620837..46e5255 100644
--- a/sdk/lib/_internal/pub/lib/src/io.dart
+++ b/sdk/lib/_internal/pub/lib/src/io.dart
@@ -6,6 +6,7 @@
library pub.io;
import 'dart:async';
+import 'dart:collection';
import 'dart:io';
import 'dart:isolate';
import 'dart:json';
@@ -40,6 +41,103 @@
/// for a symlink only if that symlink is unbroken and points to a file.
bool fileExists(String file) => new File(file).existsSync();
+/// Returns the canonical path for [pathString]. This is the normalized,
+/// absolute path, with symlinks resolved. As in [transitiveTarget], broken or
+/// recursive symlinks will not be fully resolved.
+///
+/// This doesn't require [pathString] to point to a path that exists on the
+/// filesystem; nonexistent or unreadable path entries are treated as normal
+/// directories.
+String canonicalize(String pathString) {
+ var seen = new Set<String>();
+ var components = new Queue<String>.from(
+ path.split(path.normalize(path.absolute(pathString))));
+
+ // The canonical path, built incrementally as we iterate through [components].
+ var newPath = components.removeFirst();
+
+ // Move through the components of the path, resolving each one's symlinks as
+ // necessary. A resolved component may also add new components that need to be
+ // resolved in turn.
+ while (!components.isEmpty) {
+ seen.add(path.join(newPath, path.joinAll(components)));
+ var resolvedPath = resolveLink(
+ path.join(newPath, components.removeFirst()));
+ var relative = path.relative(resolvedPath, from: newPath);
+
+ // If the resolved path of the component relative to `newPath` is just ".",
+ // that means component was a symlink pointing to its parent directory. We
+ // can safely ignore such components.
+ if (relative == '.') continue;
+
+ var relativeComponents = new Queue<String>.from(path.split(relative));
+
+ // If the resolved path is absolute relative to `newPath`, that means it's
+ // on a different drive. We need to canonicalize the entire target of that
+ // symlink again.
+ if (path.isAbsolute(relative)) {
+ // If we've already tried to canonicalize the new path, we've encountered
+ // a symlink loop. Avoid going infinite by treating the recursive symlink
+ // as the canonical path.
+ if (seen.contains(relative)) {
+ newPath = relative;
+ } else {
+ newPath = relativeComponents.removeFirst();
+ relativeComponents.addAll(components);
+ components = relativeComponents;
+ }
+ continue;
+ }
+
+ // Pop directories off `newPath` if the component links upwards in the
+ // directory hierarchy.
+ while (relativeComponents.first == '..') {
+ newPath = path.dirname(newPath);
+ relativeComponents.removeFirst();
+ }
+
+ // If there's only one component left, [resolveLink] guarantees that it's
+ // not a link (or is a broken link). We can just add it to `newPath` and
+ // continue resolving the remaining components.
+ if (relativeComponents.length == 1) {
+ newPath = path.join(newPath, relativeComponents.single);
+ continue;
+ }
+
+ // If we've already tried to canonicalize the new path, we've encountered a
+ // symlink loop. Avoid going infinite by treating the recursive symlink as
+ // the canonical path.
+ var newSubPath = path.join(newPath, path.joinAll(relativeComponents));
+ if (seen.contains(newSubPath)) {
+ newPath = newSubPath;
+ continue;
+ }
+
+ // If there are multiple new components to resolve, add them to the
+ // beginning of the queue.
+ relativeComponents.addAll(components);
+ components = relativeComponents;
+ }
+ return newPath;
+}
+
+/// Returns the transitive target of [link] (if A links to B which links to C,
+/// this will return C). If [link] is part of a symlink loop (e.g. A links to B
+/// which links back to A), this returns the path to the first repeated link (so
+/// `transitiveTarget("A")` would return `"A"` and `transitiveTarget("A")` would
+/// return `"B"`).
+///
+/// This accepts paths to non-links or broken links, and returns them as-is.
+String resolveLink(String link) {
+ var seen = new Set<String>();
+ while (linkExists(link) && !seen.contains(link)) {
+ seen.add(link);
+ link = path.normalize(path.join(
+ path.dirname(link), new Link(link).targetSync()));
+ }
+ return link;
+}
+
/// Reads the contents of the text file [file].
String readTextFile(String file) =>
new File(file).readAsStringSync(encoding: Encoding.UTF_8);
@@ -137,7 +235,7 @@
var contents = <String>[];
// Avoid recursive symlinks.
- var resolvedPath = new File(dir).fullPathSync();
+ var resolvedPath = canonicalize(dir);
if (listedDirectories.contains(resolvedPath)) return [];
listedDirectories = new Set<String>.from(listedDirectories);
diff --git a/sdk/lib/_internal/pub/lib/src/path_source.dart b/sdk/lib/_internal/pub/lib/src/path_source.dart
index 72d4065..2b66e3b 100644
--- a/sdk/lib/_internal/pub/lib/src/path_source.dart
+++ b/sdk/lib/_internal/pub/lib/src/path_source.dart
@@ -32,18 +32,10 @@
}
bool descriptionsEqual(description1, description2) {
- try {
- // Compare real paths after normalizing and resolving symlinks.
- var path1 = new File(description1["path"]).fullPathSync();
- var path2 = new File(description2["path"]).fullPathSync();
- return path1 == path2;
- } on FileIOException catch (ex) {
- // If either of the files couldn't be found, fall back to just comparing
- // the normalized paths.
- var path1 = path.normalize(path.absolute(description1["path"]));
- var path2 = path.normalize(path.absolute(description2["path"]));
- return path1 == path2;
- }
+ // Compare real paths after normalizing and resolving symlinks.
+ var path1 = canonicalize(description1["path"]);
+ var path2 = canonicalize(description2["path"]);
+ return path1 == path2;
}
Future<bool> install(PackageId id, String destination) {
@@ -110,18 +102,17 @@
/// Ensures that [description] is a valid path description. It must be a map,
/// with a "path" key containing a path that points to an existing directory.
- /// Throws a [FormatException] if the path is invalid.
+ /// Throws an [ApplicationException] if the path is invalid.
void _validatePath(String name, description) {
var dir = description["path"];
if (dirExists(dir)) return;
if (fileExists(dir)) {
- throw new FormatException(
- "Path dependency for package '$name' must refer to a "
- "directory, not a file. Was '$dir'.");
+ fail("Path dependency for package '$name' must refer to a "
+ "directory, not a file. Was '$dir'.");
}
- throw new FormatException("Could not find package '$name' at '$dir'.");
+ fail("Could not find package '$name' at '$dir'.");
}
}
diff --git a/sdk/lib/_internal/pub/lib/src/system_cache.dart b/sdk/lib/_internal/pub/lib/src/system_cache.dart
index 8a51912..567803b 100644
--- a/sdk/lib/_internal/pub/lib/src/system_cache.dart
+++ b/sdk/lib/_internal/pub/lib/src/system_cache.dart
@@ -45,11 +45,19 @@
: _pendingInstalls = new Map<PackageId, Future<Package>>(),
sources = new SourceRegistry();
- /// Creates a system cache and registers the standard set of sources.
- factory SystemCache.withSources(String rootDir) {
+ /// Creates a system cache and registers the standard set of sources. If
+ /// [isOffline] is `true`, then the offline hosted source will be used.
+ /// Defaults to `false`.
+ factory SystemCache.withSources(String rootDir, {bool isOffline: false}) {
var cache = new SystemCache(rootDir);
cache.register(new GitSource());
- cache.register(new HostedSource());
+
+ if (isOffline) {
+ cache.register(new OfflineHostedSource());
+ } else {
+ cache.register(new HostedSource());
+ }
+
cache.register(new PathSource());
cache.sources.setDefault('hosted');
return cache;
diff --git a/sdk/lib/_internal/pub/lib/src/validator/directory.dart b/sdk/lib/_internal/pub/lib/src/validator/directory.dart
index 216f2bf..5e90437 100644
--- a/sdk/lib/_internal/pub/lib/src/validator/directory.dart
+++ b/sdk/lib/_internal/pub/lib/src/validator/directory.dart
@@ -18,7 +18,9 @@
DirectoryValidator(Entrypoint entrypoint)
: super(entrypoint);
- static final _PLURAL_NAMES = ["tools", "tests", "docs", "examples"];
+ static final _PLURAL_NAMES = [
+ "benchmarks", "docs", "examples", "tests", "tools"
+ ];
Future validate() {
return new Future.sync(() {
diff --git a/sdk/lib/_internal/pub/pub.status b/sdk/lib/_internal/pub/pub.status
index b170e83..00fafbc 100644
--- a/sdk/lib/_internal/pub/pub.status
+++ b/sdk/lib/_internal/pub/pub.status
@@ -12,6 +12,3 @@
# Pub only runs on the standalone VM, not the browser.
[ $runtime == drt || $runtime == dartium || $runtime == opera ]
*: Skip
-
-[ $system == windows ]
-test/io_test: Fail # Issue 7505
diff --git a/sdk/lib/_internal/pub/test/descriptor.dart b/sdk/lib/_internal/pub/test/descriptor.dart
index 4122bbf..bccc72d 100644
--- a/sdk/lib/_internal/pub/test/descriptor.dart
+++ b/sdk/lib/_internal/pub/test/descriptor.dart
@@ -83,14 +83,6 @@
]);
}
-/// Describes a directory for a package installed from the mock package server.
-/// This directory is of the form found in the global package cache.
-Descriptor packageCacheDir(String name, String version) {
- return dir("$name-$version", [
- libDir(name, '$name $version')
- ]);
-}
-
/// Describes a directory for a Git package. This directory is of the form
/// found in the revision cache of the global package cache.
Descriptor gitPackageRevisionCacheDir(String name, [int modifier]) {
@@ -137,14 +129,25 @@
///
/// A package's value may also be a list of versions, in which case all
/// versions are expected to be installed.
-Descriptor cacheDir(Map packages) {
+///
+/// If [includePubspecs] is `true`, then pubspecs will be created for each
+/// package. Defaults to `false` so that the contents of pubspecs are not
+/// validated since they will often lack the dependencies section that the
+/// real pubspec being compared against has. You usually only need to pass
+/// `true` for this if you plan to call [create] on the resulting descriptor.
+Descriptor cacheDir(Map packages, {bool includePubspecs: false}) {
var contents = <Descriptor>[];
packages.forEach((name, versions) {
if (versions is! List) versions = [versions];
for (var version in versions) {
- contents.add(packageCacheDir(name, version));
+ var packageContents = [libDir(name, '$name $version')];
+ if (includePubspecs) {
+ packageContents.add(libPubspec(name, version));
+ }
+ contents.add(dir("$name-$version", packageContents));
}
});
+
return dir(cachePath, [
dir('hosted', [
async(port.then((p) => dir('localhost%58$p', contents)))
diff --git a/sdk/lib/_internal/pub/test/error_group_test.dart b/sdk/lib/_internal/pub/test/error_group_test.dart
index db952c8..ff2f699 100644
--- a/sdk/lib/_internal/pub/test/error_group_test.dart
+++ b/sdk/lib/_internal/pub/test/error_group_test.dart
@@ -63,10 +63,12 @@
"been called", () {
completer.complete('value');
- expect(() => errorGroup.registerFuture(new Future.value()),
- throwsStateError);
- expect(() => errorGroup.registerStream(new StreamController().stream),
- throwsStateError);
+ completer.future.then(expectAsync1((_) {
+ expect(() => errorGroup.registerFuture(new Future.value()),
+ throwsStateError);
+ expect(() => errorGroup.registerStream(new StreamController().stream),
+ throwsStateError);
+ }));
});
test('should pass through an exception from the future if it has a '
diff --git a/sdk/lib/_internal/pub/test/install/hosted/offline_test.dart b/sdk/lib/_internal/pub/test/install/hosted/offline_test.dart
new file mode 100644
index 0000000..a39ed74
--- /dev/null
+++ b/sdk/lib/_internal/pub/test/install/hosted/offline_test.dart
@@ -0,0 +1,66 @@
+// 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.
+
+library pub_tests;
+
+import 'dart:io';
+
+import '../../descriptor.dart' as d;
+import '../../test_pub.dart';
+
+main() {
+ initConfig();
+ integration('installs a package using the cache', () {
+ // Run the server so that we know what URL to use in the system cache.
+ servePackages([]);
+
+ d.cacheDir({
+ "foo": ["1.2.2", "1.2.3"],
+ "bar": ["1.2.3"]
+ }, includePubspecs: true).create();
+
+ d.appDir([
+ dependencyMap("foo", "any"),
+ dependencyMap("bar", "any")
+ ]).create();
+
+ schedulePub(args: ['install', '--offline'],
+ output: new RegExp("Dependencies installed!\$"));
+
+ d.packagesDir({
+ "foo": "1.2.3",
+ "bar": "1.2.3"
+ }).validate();
+ });
+
+ integration('fails gracefully if a dependency is not cached', () {
+ // Run the server so that we know what URL to use in the system cache.
+ servePackages([]);
+
+ d.appDir([
+ dependencyMap("foo", "any")
+ ]).create();
+
+ schedulePub(args: ['install', '--offline'],
+ error: new RegExp('Could not find package "foo" in cache'),
+ exitCode: 1);
+ });
+
+ integration('fails gracefully no cached versions match', () {
+ // Run the server so that we know what URL to use in the system cache.
+ servePackages([]);
+
+ d.cacheDir({
+ "foo": ["1.2.2", "1.2.3"]
+ }, includePubspecs: true).create();
+
+ d.appDir([
+ dependencyMap("foo", ">2.0.0")
+ ]).create();
+
+ schedulePub(args: ['install', '--offline'],
+ error: new RegExp("Package 'foo' has no versions that match >2.0.0"),
+ exitCode: 1);
+ });
+}
diff --git a/sdk/lib/_internal/pub/test/install/path/nonexistent_dir_test.dart b/sdk/lib/_internal/pub/test/install/path/nonexistent_dir_test.dart
index 7220cb9..053c922 100644
--- a/sdk/lib/_internal/pub/test/install/path/nonexistent_dir_test.dart
+++ b/sdk/lib/_internal/pub/test/install/path/nonexistent_dir_test.dart
@@ -7,7 +7,6 @@
import 'package:pathos/path.dart' as path;
import 'package:scheduled_test/scheduled_test.dart';
-import '../../../lib/src/exit_codes.dart' as exit_codes;
import '../../descriptor.dart' as d;
import '../../test_pub.dart';
@@ -34,6 +33,6 @@
schedulePub(args: ['install'],
error:
new RegExp("Could not find package 'foo' at '$escapePath'."),
- exitCode: exit_codes.DATA);
+ exitCode: 1);
});
}
\ No newline at end of file
diff --git a/sdk/lib/_internal/pub/test/install/path/path_is_file_test.dart b/sdk/lib/_internal/pub/test/install/path/path_is_file_test.dart
index 32f7180..7779467 100644
--- a/sdk/lib/_internal/pub/test/install/path/path_is_file_test.dart
+++ b/sdk/lib/_internal/pub/test/install/path/path_is_file_test.dart
@@ -5,7 +5,6 @@
import 'package:pathos/path.dart' as path;
import 'package:scheduled_test/scheduled_test.dart';
-import '../../../lib/src/exit_codes.dart' as exit_codes;
import '../../descriptor.dart' as d;
import '../../test_pub.dart';
@@ -38,6 +37,6 @@
schedulePub(args: ['install'],
error: new RegExp("Path dependency for package 'foo' must refer to a "
"directory, not a file. Was '$escapePath'."),
- exitCode: exit_codes.DATA);
+ exitCode: 1);
});
}
\ No newline at end of file
diff --git a/sdk/lib/_internal/pub/test/install/path/shared_dependency_symlink_test.dart b/sdk/lib/_internal/pub/test/install/path/shared_dependency_symlink_test.dart
index 9cb3a9b..d3b75da 100644
--- a/sdk/lib/_internal/pub/test/install/path/shared_dependency_symlink_test.dart
+++ b/sdk/lib/_internal/pub/test/install/path/shared_dependency_symlink_test.dart
@@ -10,10 +10,6 @@
import '../../test_pub.dart';
main() {
- // new File().fullPathSync() does not resolve through NTFS junction points,
- // so this feature does not work on Windows.
- if (Platform.operatingSystem == "windows") return;
-
initConfig();
integration("shared dependency with symlink", () {
d.dir("shared", [
diff --git a/sdk/lib/_internal/pub/test/install/pub_install_test.dart b/sdk/lib/_internal/pub/test/install/pub_install_test.dart
index a20f26ef..866376b 100644
--- a/sdk/lib/_internal/pub/test/install/pub_install_test.dart
+++ b/sdk/lib/_internal/pub/test/install/pub_install_test.dart
@@ -173,117 +173,35 @@
});
group('creates a packages directory in', () {
- integration('"test/" and its subdirectories', () {
- d.dir(appPath, [
- d.appPubspec([]),
- d.libDir('foo'),
- d.dir("test", [d.dir("subtest")])
- ]).create();
+ for (var dir in ["benchmark", "example", "test", "tool", "web"]) {
+ integration('"$dir/" and its subdirectories', () {
+ d.dir(appPath, [
+ d.appPubspec([]),
+ d.libDir('foo'),
+ d.dir(dir, [d.dir("sub${dir}")])
+ ]).create();
- schedulePub(args: ['install'],
- output: new RegExp(r"Dependencies installed!$"));
+ schedulePub(args: ['install'],
+ output: new RegExp(r"Dependencies installed!$"));
- d.dir(appPath, [
- d.dir("test", [
- d.dir("packages", [
- d.dir("myapp", [
- d.file('foo.dart', 'main() => "foo";')
- ])
- ]),
- d.dir("subtest", [
+ d.dir(appPath, [
+ d.dir(dir, [
d.dir("packages", [
d.dir("myapp", [
d.file('foo.dart', 'main() => "foo";')
])
- ])
- ])
- ])
- ]).validate();
- });
-
- integration('"example/" and its subdirectories', () {
- d.dir(appPath, [
- d.appPubspec([]),
- d.libDir('foo'),
- d.dir("example", [d.dir("subexample")])
- ]).create();
-
- schedulePub(args: ['install'],
- output: new RegExp(r"Dependencies installed!$"));
-
- d.dir(appPath, [
- d.dir("example", [
- d.dir("packages", [
- d.dir("myapp", [
- d.file('foo.dart', 'main() => "foo";')
- ])
- ]),
- d.dir("subexample", [
- d.dir("packages", [
- d.dir("myapp", [
- d.file('foo.dart', 'main() => "foo";')
+ ]),
+ d.dir("sub${dir}", [
+ d.dir("packages", [
+ d.dir("myapp", [
+ d.file('foo.dart', 'main() => "foo";')
+ ])
])
])
])
- ])
- ]).validate();
- });
-
- integration('"tool/" and its subdirectories', () {
- d.dir(appPath, [
- d.appPubspec([]),
- d.libDir('foo'),
- d.dir("tool", [d.dir("subtool")])
- ]).create();
-
- schedulePub(args: ['install'],
- output: new RegExp(r"Dependencies installed!$"));
-
- d.dir(appPath, [
- d.dir("tool", [
- d.dir("packages", [
- d.dir("myapp", [
- d.file('foo.dart', 'main() => "foo";')
- ])
- ]),
- d.dir("subtool", [
- d.dir("packages", [
- d.dir("myapp", [
- d.file('foo.dart', 'main() => "foo";')
- ])
- ])
- ])
- ])
- ]).validate();
- });
-
- integration('"web/" and its subdirectories', () {
- d.dir(appPath, [
- d.appPubspec([]),
- d.libDir('foo'),
- d.dir("web", [d.dir("subweb")])
- ]).create();
-
- schedulePub(args: ['install'],
- output: new RegExp(r"Dependencies installed!$"));
-
- d.dir(appPath, [
- d.dir("web", [
- d.dir("packages", [
- d.dir("myapp", [
- d.file('foo.dart', 'main() => "foo";')
- ])
- ]),
- d.dir("subweb", [
- d.dir("packages", [
- d.dir("myapp", [
- d.file('foo.dart', 'main() => "foo";')
- ])
- ])
- ])
- ])
- ]).validate();
- });
+ ]).validate();
+ });
+ }
integration('"bin/"', () {
d.dir(appPath, [
diff --git a/sdk/lib/_internal/pub/test/io_test.dart b/sdk/lib/_internal/pub/test/io_test.dart
index 3a0506c..6aa82bc 100644
--- a/sdk/lib/_internal/pub/test/io_test.dart
+++ b/sdk/lib/_internal/pub/test/io_test.dart
@@ -4,6 +4,7 @@
library io_test;
+import 'dart:async';
import 'dart:io';
import 'package:pathos/path.dart' as path;
@@ -135,6 +136,154 @@
});
});
+ group('canonicalize', () {
+ test('resolves a non-link', () {
+ expect(withCanonicalTempDir((temp) {
+ var filePath = path.join(temp, 'file');
+ writeTextFile(filePath, '');
+ expect(canonicalize(filePath), equals(filePath));
+ }), completes);
+ });
+
+ test('resolves a non-existent file', () {
+ expect(withCanonicalTempDir((temp) {
+ expect(canonicalize(path.join(temp, 'nothing')),
+ equals(path.join(temp, 'nothing')));
+ }), completes);
+ });
+
+ test('resolves a symlink', () {
+ expect(withCanonicalTempDir((temp) {
+ createDir(path.join(temp, 'linked-dir'));
+ createSymlink(
+ path.join(temp, 'linked-dir'),
+ path.join(temp, 'dir'));
+ expect(
+ canonicalize(path.join(temp, 'dir')),
+ equals(path.join(temp, 'linked-dir')));
+ }), completes);
+ });
+
+ test('resolves a relative symlink', () {
+ expect(withCanonicalTempDir((temp) {
+ createDir(path.join(temp, 'linked-dir'));
+ createSymlink(
+ path.join(temp, 'linked-dir'),
+ path.join(temp, 'dir'),
+ relative: true);
+ expect(
+ canonicalize(path.join(temp, 'dir')),
+ equals(path.join(temp, 'linked-dir')));
+ }), completes);
+ });
+
+ test('resolves a single-level horizontally recursive symlink', () {
+ expect(withCanonicalTempDir((temp) {
+ var linkPath = path.join(temp, 'foo');
+ createSymlink(linkPath, linkPath);
+ expect(canonicalize(linkPath), equals(linkPath));
+ }), completes);
+ });
+
+ test('resolves a multi-level horizontally recursive symlink', () {
+ expect(withCanonicalTempDir((temp) {
+ var fooPath = path.join(temp, 'foo');
+ var barPath = path.join(temp, 'bar');
+ var bazPath = path.join(temp, 'baz');
+ createSymlink(barPath, fooPath);
+ createSymlink(bazPath, barPath);
+ createSymlink(fooPath, bazPath);
+ expect(canonicalize(fooPath), equals(fooPath));
+ expect(canonicalize(barPath), equals(barPath));
+ expect(canonicalize(bazPath), equals(bazPath));
+
+ createSymlink(fooPath, path.join(temp, 'outer'));
+ expect(canonicalize(path.join(temp, 'outer')), equals(fooPath));
+ }), completes);
+ });
+
+ test('resolves a broken symlink', () {
+ expect(withCanonicalTempDir((temp) {
+ createSymlink(path.join(temp, 'nonexistent'), path.join(temp, 'foo'));
+ expect(
+ canonicalize(path.join(temp, 'foo')),
+ equals(path.join(temp, 'nonexistent')));
+ }), completes);
+ });
+
+ test('resolves multiple nested symlinks', () {
+ expect(withCanonicalTempDir((temp) {
+ var dir1 = path.join(temp, 'dir1');
+ var dir2 = path.join(temp, 'dir2');
+ var subdir1 = path.join(dir1, 'subdir1');
+ var subdir2 = path.join(dir2, 'subdir2');
+ createDir(dir2);
+ createDir(subdir2);
+ createSymlink(dir2, dir1);
+ createSymlink(subdir2, subdir1);
+ expect(
+ canonicalize(path.join(subdir1, 'file')),
+ equals(path.join(subdir2, 'file')));
+ }), completes);
+ });
+
+ test('resolves a nested vertical symlink', () {
+ expect(withCanonicalTempDir((temp) {
+ var dir1 = path.join(temp, 'dir1');
+ var dir2 = path.join(temp, 'dir2');
+ var subdir = path.join(dir1, 'subdir');
+ createDir(dir1);
+ createDir(dir2);
+ createSymlink(dir2, subdir);
+ expect(
+ canonicalize(path.join(subdir, 'file')),
+ equals(path.join(dir2, 'file')));
+ }), completes);
+ });
+
+ test('resolves a vertically recursive symlink', () {
+ expect(withCanonicalTempDir((temp) {
+ var dir = path.join(temp, 'dir');
+ var subdir = path.join(dir, 'subdir');
+ createDir(dir);
+ createSymlink(dir, subdir);
+ expect(
+ canonicalize(path.join(temp, 'dir', 'subdir', 'subdir', 'subdir',
+ 'subdir', 'file')),
+ equals(path.join(dir, 'file')));
+ }), completes);
+ });
+
+ test('resolves a symlink that links to a path that needs more resolving',
+ () {
+ expect(withCanonicalTempDir((temp) {
+ var dir = path.join(temp, 'dir');
+ var linkdir = path.join(temp, 'linkdir');
+ var linkfile = path.join(dir, 'link');
+ createDir(dir);
+ createSymlink(dir, linkdir);
+ createSymlink(path.join(linkdir, 'file'), linkfile);
+ expect(
+ canonicalize(linkfile),
+ equals(path.join(dir, 'file')));
+ }), completes);
+ });
+
+ test('resolves a pair of pathologically-recursive symlinks', () {
+ expect(withCanonicalTempDir((temp) {
+ var foo = path.join(temp, 'foo');
+ var subfoo = path.join(foo, 'subfoo');
+ var bar = path.join(temp, 'bar');
+ var subbar = path.join(bar, 'subbar');
+ createSymlink(subbar, foo);
+ createSymlink(subfoo, bar);
+ expect(
+ canonicalize(subfoo),
+ equals(path.join(subfoo, 'subbar', 'subfoo')));
+ }), completes);
+ });
+ });
+
testExistencePredicate("entryExists", entryExists,
forFile: true,
forFileSymlink: true,
@@ -278,3 +427,7 @@
}
});
}
+
+/// Like [withTempDir], but canonicalizes the path before passing it to [fn].
+Future withCanonicalTempDir(Future fn(String path)) =>
+ withTempDir((temp) => fn(canonicalize(temp)));
diff --git a/sdk/lib/_internal/pub/test/pub_test.dart b/sdk/lib/_internal/pub/test/pub_test.dart
index 36071ba..66eba69 100644
--- a/sdk/lib/_internal/pub/test/pub_test.dart
+++ b/sdk/lib/_internal/pub/test/pub_test.dart
@@ -27,7 +27,7 @@
[normal] Show errors, warnings, and user messages.
[solver] Show steps during version resolution.
- -v, --verbose Shortcut for "--verbosity=all"
+ -v, --verbose Shortcut for "--verbosity=all".
Available commands:
cache Inspect the system cache.
@@ -100,6 +100,7 @@
Install the current package's dependencies.
Usage: pub install
+ --[no-]offline Use cached packages instead of accessing the network.
''');
});
@@ -109,9 +110,9 @@
Publish the current package to pub.dartlang.org.
Usage: pub publish [options]
- -n, --dry-run Validate but do not publish the package
- -f, --force Publish without confirmation if there are no errors
- --server The package server to which to upload this package
+ -n, --dry-run Validate but do not publish the package.
+ -f, --force Publish without confirmation if there are no errors.
+ --server The package server to which to upload this package.
(defaults to "https://pub.dartlang.org")
''');
});
diff --git a/sdk/lib/_internal/pub/test/test_pub.dart b/sdk/lib/_internal/pub/test/test_pub.dart
index a175c7b..e2a06f5 100644
--- a/sdk/lib/_internal/pub/test/test_pub.dart
+++ b/sdk/lib/_internal/pub/test/test_pub.dart
@@ -355,7 +355,7 @@
// If the executable looks like a path, get its full path. That way we
// can still find it when we spawn it with a different working directory.
if (dartBin.contains(Platform.pathSeparator)) {
- dartBin = new File(dartBin).fullPathSync();
+ dartBin = path.absolute(dartBin);
}
// Find the main pub entrypoint.
diff --git a/sdk/lib/_internal/pub/test/update/hosted/offline_test.dart b/sdk/lib/_internal/pub/test/update/hosted/offline_test.dart
new file mode 100644
index 0000000..50686d0
--- /dev/null
+++ b/sdk/lib/_internal/pub/test/update/hosted/offline_test.dart
@@ -0,0 +1,68 @@
+// 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.
+
+library pub_tests;
+
+import 'dart:io';
+
+import '../../descriptor.dart' as d;
+import '../../test_pub.dart';
+
+main() {
+ initConfig();
+ integration('updates a package using the cache', () {
+ // Run the server so that we know what URL to use in the system cache.
+ servePackages([]);
+
+ d.cacheDir({
+ "foo": ["1.2.2", "1.2.3"],
+ "bar": ["1.2.3"]
+ }, includePubspecs: true).create();
+
+ d.appDir([
+ dependencyMap("foo", "any"),
+ dependencyMap("bar", "any")
+ ]).create();
+
+ schedulePub(args: ['update', '--offline'],
+ output: new RegExp("Dependencies updated!\$"),
+ error: "Warning: Updating when offline may not update you "
+ "to the latest versions of your dependencies.");
+
+ d.packagesDir({
+ "foo": "1.2.3",
+ "bar": "1.2.3"
+ }).validate();
+ });
+
+ integration('fails gracefully if a dependency is not cached', () {
+ // Run the server so that we know what URL to use in the system cache.
+ servePackages([]);
+
+ d.appDir([
+ dependencyMap("foo", "any")
+ ]).create();
+
+ schedulePub(args: ['update', '--offline'],
+ error: new RegExp('Could not find package "foo" in cache'),
+ exitCode: 1);
+ });
+
+ integration('fails gracefully no cached versions match', () {
+ // Run the server so that we know what URL to use in the system cache.
+ servePackages([]);
+
+ d.cacheDir({
+ "foo": ["1.2.2", "1.2.3"]
+ }, includePubspecs: true).create();
+
+ d.appDir([
+ dependencyMap("foo", ">2.0.0")
+ ]).create();
+
+ schedulePub(args: ['update', '--offline'],
+ error: new RegExp("Package 'foo' has no versions that match >2.0.0"),
+ exitCode: 1);
+ });
+}
diff --git a/sdk/lib/_internal/pub/test/validator/directory_test.dart b/sdk/lib/_internal/pub/test/validator/directory_test.dart
index e3acfb4..1b58b69 100644
--- a/sdk/lib/_internal/pub/test/validator/directory_test.dart
+++ b/sdk/lib/_internal/pub/test/validator/directory_test.dart
@@ -35,7 +35,10 @@
'named', () {
setUp(d.validPackage.create);
- var names = ["tools", "tests", "docs", "examples", "sample", "samples"];
+ var names = [
+ "benchmarks", "docs", "examples", "sample", "samples", "tests", "tools"
+ ];
+
for (var name in names) {
integration('"$name"', () {
d.dir(appPath, [d.dir(name)]).create();
diff --git a/sdk/lib/async/future.dart b/sdk/lib/async/future.dart
index 7219719..59f0efb 100644
--- a/sdk/lib/async/future.dart
+++ b/sdk/lib/async/future.dart
@@ -343,7 +343,38 @@
*/
abstract class Completer<T> {
- factory Completer() => new _CompleterImpl<T>();
+ factory Completer() => new _AsyncCompleter<T>();
+
+ /**
+ * Completes the future synchronously.
+ *
+ * This constructor should be avoided unless the completion of the future is
+ * known to be the final result of another asynchronous operation. If in doubt
+ * use the default [Completer] constructor.
+ *
+ * Example:
+ *
+ * var completer = new Completer.sync();
+ * // The completion is the result of the asynchronous onDone event.
+ * // No other operation is performed after the completion. It is safe
+ * // to use the Completer.sync constructor.
+ * stream.listen(print, onDone: () { completer.complete("done"); });
+ *
+ * Bad example. Do not use this code. Only for illustrative purposes:
+ *
+ * var completer = new Completer.sync();
+ * // The completion is the result of the asynchronous onDone event.
+ * // However, there is still code executed after the completion. This
+ * // operation is *not* safe.
+ * stream.listen(print, onDone: () {
+ * completer.complete("done");
+ * foo(); // This operation follows the completion.
+ * });
+ *
+ * *WARNING* This constructor is experimental and could disappear or change
+ * behavior.
+ */
+ factory Completer.sync() => new _SyncCompleter<T>();
/** The future that will contain the result provided to this completer. */
Future get future;
diff --git a/sdk/lib/async/future_impl.dart b/sdk/lib/async/future_impl.dart
index 3660b9f..b4f7f18 100644
--- a/sdk/lib/async/future_impl.dart
+++ b/sdk/lib/async/future_impl.dart
@@ -7,17 +7,19 @@
deprecatedFutureValue(_FutureImpl future) =>
future._isComplete ? future._resultOrListeners : null;
-class _CompleterImpl<T> implements Completer<T> {
+abstract class _Completer<T> implements Completer<T> {
final Future<T> future;
bool _isComplete = false;
- _CompleterImpl() : future = new _FutureImpl<T>();
+ _Completer() : future = new _FutureImpl<T>();
+
+ void _setFutureValue(T value);
+ void _setFutureError(error);
void complete([T value]) {
if (_isComplete) throw new StateError("Future already completed");
_isComplete = true;
- _FutureImpl future = this.future;
- future._setValue(value);
+ _setFutureValue(value);
}
void completeError(Object error, [Object stackTrace = null]) {
@@ -27,13 +29,36 @@
// Force the stack trace onto the error, even if it already had one.
_attachStackTrace(error, stackTrace);
}
- _FutureImpl future = this.future;
- future._setError(error);
+ _setFutureError(error);
}
bool get isCompleted => _isComplete;
}
+class _AsyncCompleter<T> extends _Completer<T> {
+ void _setFutureValue(T value) {
+ _FutureImpl future = this.future;
+ runAsync(() { future._setValue(value); });
+ }
+
+ void _setFutureError(error) {
+ _FutureImpl future = this.future;
+ runAsync(() { future._setError(error); });
+ }
+}
+
+class _SyncCompleter<T> extends _Completer<T> {
+ void _setFutureValue(T value) {
+ _FutureImpl future = this.future;
+ future._setValue(value);
+ }
+
+ void _setFutureError(error) {
+ _FutureImpl future = this.future;
+ future._setError(error);
+ }
+}
+
/**
* A listener on a future.
*
diff --git a/sdk/lib/html/dart2js/html_dart2js.dart b/sdk/lib/html/dart2js/html_dart2js.dart
index d67a2c9..bd7050c 100644
--- a/sdk/lib/html/dart2js/html_dart2js.dart
+++ b/sdk/lib/html/dart2js/html_dart2js.dart
@@ -187,6 +187,9 @@
@DocsEditable
@DomName('WebKitAnimationEvent')
+@SupportedBrowser(SupportedBrowser.CHROME)
+@SupportedBrowser(SupportedBrowser.SAFARI)
+@Experimental
class AnimationEvent extends Event native "WebKitAnimationEvent" {
@DomName('AnimationEvent.animationName')
@@ -982,7 +985,7 @@
@DomName('CanvasRenderingContext2D.currentPath')
@DocsEditable
- DomPath currentPath;
+ Path currentPath;
@DomName('CanvasRenderingContext2D.fillStyle')
@DocsEditable
@@ -1826,6 +1829,22 @@
@DocsEditable
+@DomName('WebKitCSSFilterRule')
+@SupportedBrowser(SupportedBrowser.CHROME)
+@SupportedBrowser(SupportedBrowser.SAFARI)
+@Experimental
+class CssFilterRule extends CssRule native "WebKitCSSFilterRule" {
+
+ @DomName('WebKitCSSFilterRule.style')
+ @DocsEditable
+ final CssStyleDeclaration style;
+}
+// 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.
+
+
+@DocsEditable
@DomName('CSSFontFaceLoadEvent')
class CssFontFaceLoadEvent extends Event native "CSSFontFaceLoadEvent" {
@@ -1903,6 +1922,9 @@
@DocsEditable
@DomName('WebKitCSSKeyframeRule')
+@SupportedBrowser(SupportedBrowser.CHROME)
+@SupportedBrowser(SupportedBrowser.SAFARI)
+@Experimental
class CssKeyframeRule extends CssRule native "WebKitCSSKeyframeRule" {
@DomName('WebKitCSSKeyframeRule.keyText')
@@ -1920,6 +1942,9 @@
@DocsEditable
@DomName('WebKitCSSKeyframesRule')
+@SupportedBrowser(SupportedBrowser.CHROME)
+@SupportedBrowser(SupportedBrowser.SAFARI)
+@Experimental
class CssKeyframesRule extends CssRule native "WebKitCSSKeyframesRule" {
@DomName('WebKitCSSKeyframesRule.cssRules')
@@ -1994,6 +2019,24 @@
@DocsEditable
+@DomName('WebKitCSSRegionRule')
+@SupportedBrowser(SupportedBrowser.CHROME)
+@SupportedBrowser(SupportedBrowser.SAFARI)
+@Experimental
+class CssRegionRule extends CssRule native "WebKitCSSRegionRule" {
+
+ @DomName('WebKitCSSRegionRule.cssRules')
+ @DocsEditable
+ @Returns('_CssRuleList')
+ @Creates('_CssRuleList')
+ final List<CssRule> cssRules;
+}
+// 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.
+
+
+@DocsEditable
@DomName('CSSRule')
class CssRule native "CSSRule" {
@@ -6041,7 +6084,7 @@
@DomName('Document.securityPolicy')
@DocsEditable
- final DomSecurityPolicy securityPolicy;
+ final SecurityPolicy securityPolicy;
@JSName('selectedStylesheetSet')
@DomName('Document.selectedStylesheetSet')
@@ -6301,7 +6344,7 @@
@SupportedBrowser(SupportedBrowser.CHROME)
@SupportedBrowser(SupportedBrowser.SAFARI)
@Experimental
- DomNamedFlowCollection getNamedFlows() native;
+ NamedFlowCollection getNamedFlows() native;
@DomName('Document.webkitRegister')
@DocsEditable
@@ -6541,7 +6584,7 @@
* For details about CSS selector syntax, see the
* [CSS selector specification](http://www.w3.org/TR/css3-selectors/).
*/
- List<Element> queryAll(String selectors) {
+ ElementList queryAll(String selectors) {
return new _FrozenElementList._wrap($dom_querySelectorAll(selectors));
}
}
@@ -6744,273 +6787,6 @@
@DocsEditable
-@DomName('MimeType')
-class DomMimeType native "MimeType" {
-
- @DomName('DOMMimeType.description')
- @DocsEditable
- final String description;
-
- @DomName('DOMMimeType.enabledPlugin')
- @DocsEditable
- final DomPlugin enabledPlugin;
-
- @DomName('DOMMimeType.suffixes')
- @DocsEditable
- final String suffixes;
-
- @DomName('DOMMimeType.type')
- @DocsEditable
- final String type;
-}
-// 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.
-
-
-@DocsEditable
-@DomName('MimeTypeArray')
-class DomMimeTypeArray implements JavaScriptIndexingBehavior, List<DomMimeType> native "MimeTypeArray" {
-
- @DomName('DOMMimeTypeArray.length')
- @DocsEditable
- int get length => JS("int", "#.length", this);
-
- DomMimeType operator[](int index) => JS("DomMimeType", "#[#]", this, index);
-
- void operator[]=(int index, DomMimeType value) {
- throw new UnsupportedError("Cannot assign element of immutable List.");
- }
- // -- start List<DomMimeType> mixins.
- // DomMimeType is the element type.
-
- // From Iterable<DomMimeType>:
-
- Iterator<DomMimeType> get iterator {
- // Note: NodeLists are not fixed size. And most probably length shouldn't
- // be cached in both iterator _and_ forEach method. For now caching it
- // for consistency.
- return new FixedSizeListIterator<DomMimeType>(this);
- }
-
- DomMimeType reduce(DomMimeType combine(DomMimeType value, DomMimeType element)) {
- return IterableMixinWorkaround.reduce(this, combine);
- }
-
- dynamic fold(dynamic initialValue,
- dynamic combine(dynamic previousValue, DomMimeType element)) {
- return IterableMixinWorkaround.fold(this, initialValue, combine);
- }
-
- bool contains(DomMimeType element) => IterableMixinWorkaround.contains(this, element);
-
- void forEach(void f(DomMimeType element)) => IterableMixinWorkaround.forEach(this, f);
-
- String join([String separator = ""]) =>
- IterableMixinWorkaround.joinList(this, separator);
-
- Iterable map(f(DomMimeType element)) =>
- IterableMixinWorkaround.mapList(this, f);
-
- Iterable<DomMimeType> where(bool f(DomMimeType element)) =>
- IterableMixinWorkaround.where(this, f);
-
- Iterable expand(Iterable f(DomMimeType element)) =>
- IterableMixinWorkaround.expand(this, f);
-
- bool every(bool f(DomMimeType element)) => IterableMixinWorkaround.every(this, f);
-
- bool any(bool f(DomMimeType element)) => IterableMixinWorkaround.any(this, f);
-
- List<DomMimeType> toList({ bool growable: true }) =>
- new List<DomMimeType>.from(this, growable: growable);
-
- Set<DomMimeType> toSet() => new Set<DomMimeType>.from(this);
-
- bool get isEmpty => this.length == 0;
-
- Iterable<DomMimeType> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
- Iterable<DomMimeType> takeWhile(bool test(DomMimeType value)) {
- return IterableMixinWorkaround.takeWhile(this, test);
- }
-
- Iterable<DomMimeType> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
- Iterable<DomMimeType> skipWhile(bool test(DomMimeType value)) {
- return IterableMixinWorkaround.skipWhile(this, test);
- }
-
- DomMimeType firstWhere(bool test(DomMimeType value), { DomMimeType orElse() }) {
- return IterableMixinWorkaround.firstWhere(this, test, orElse);
- }
-
- DomMimeType lastWhere(bool test(DomMimeType value), {DomMimeType orElse()}) {
- return IterableMixinWorkaround.lastWhereList(this, test, orElse);
- }
-
- DomMimeType singleWhere(bool test(DomMimeType value)) {
- return IterableMixinWorkaround.singleWhere(this, test);
- }
-
- DomMimeType elementAt(int index) {
- return this[index];
- }
-
- // From Collection<DomMimeType>:
-
- void add(DomMimeType value) {
- throw new UnsupportedError("Cannot add to immutable List.");
- }
-
- void addAll(Iterable<DomMimeType> iterable) {
- throw new UnsupportedError("Cannot add to immutable List.");
- }
-
- // From List<DomMimeType>:
- void set length(int value) {
- throw new UnsupportedError("Cannot resize immutable List.");
- }
-
- void clear() {
- throw new UnsupportedError("Cannot clear immutable List.");
- }
-
- Iterable<DomMimeType> get reversed {
- return IterableMixinWorkaround.reversedList(this);
- }
-
- void sort([int compare(DomMimeType a, DomMimeType b)]) {
- throw new UnsupportedError("Cannot sort immutable List.");
- }
-
- int indexOf(DomMimeType element, [int start = 0]) =>
- Lists.indexOf(this, element, start, this.length);
-
- int lastIndexOf(DomMimeType element, [int start]) {
- if (start == null) start = length - 1;
- return Lists.lastIndexOf(this, element, start);
- }
-
- DomMimeType get first {
- if (this.length > 0) return this[0];
- throw new StateError("No elements");
- }
-
- DomMimeType get last {
- if (this.length > 0) return this[this.length - 1];
- throw new StateError("No elements");
- }
-
- DomMimeType get single {
- if (length == 1) return this[0];
- if (length == 0) throw new StateError("No elements");
- throw new StateError("More than one element");
- }
-
- void insert(int index, DomMimeType element) {
- throw new UnsupportedError("Cannot add to immutable List.");
- }
-
- void insertAll(int index, Iterable<DomMimeType> iterable) {
- throw new UnsupportedError("Cannot add to immutable List.");
- }
-
- void setAll(int index, Iterable<DomMimeType> iterable) {
- throw new UnsupportedError("Cannot modify an immutable List.");
- }
-
- DomMimeType removeAt(int pos) {
- throw new UnsupportedError("Cannot remove from immutable List.");
- }
-
- DomMimeType removeLast() {
- throw new UnsupportedError("Cannot remove from immutable List.");
- }
-
- bool remove(Object object) {
- throw new UnsupportedError("Cannot remove from immutable List.");
- }
-
- void removeWhere(bool test(DomMimeType element)) {
- throw new UnsupportedError("Cannot remove from immutable List.");
- }
-
- void retainWhere(bool test(DomMimeType element)) {
- throw new UnsupportedError("Cannot remove from immutable List.");
- }
-
- void setRange(int start, int end, Iterable<DomMimeType> iterable, [int skipCount=0]) {
- throw new UnsupportedError("Cannot setRange on immutable List.");
- }
-
- void removeRange(int start, int end) {
- throw new UnsupportedError("Cannot removeRange on immutable List.");
- }
-
- void replaceRange(int start, int end, Iterable<DomMimeType> iterable) {
- throw new UnsupportedError("Cannot modify an immutable List.");
- }
-
- void fillRange(int start, int end, [DomMimeType fillValue]) {
- throw new UnsupportedError("Cannot modify an immutable List.");
- }
-
- Iterable<DomMimeType> getRange(int start, int end) =>
- IterableMixinWorkaround.getRangeList(this, start, end);
-
- List<DomMimeType> sublist(int start, [int end]) {
- if (end == null) end = length;
- return Lists.getRange(this, start, end, <DomMimeType>[]);
- }
-
- Map<int, DomMimeType> asMap() =>
- IterableMixinWorkaround.asMapList(this);
-
- String toString() {
- StringBuffer buffer = new StringBuffer('[');
- buffer.writeAll(this, ', ');
- buffer.write(']');
- return buffer.toString();
- }
-
- // -- end List<DomMimeType> mixins.
-
- @DomName('DOMMimeTypeArray.item')
- @DocsEditable
- DomMimeType item(int index) native;
-
- @DomName('DOMMimeTypeArray.namedItem')
- @DocsEditable
- DomMimeType namedItem(String name) native;
-}
-// 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.
-
-
-@DocsEditable
-@DomName('WebKitNamedFlowCollection')
-class DomNamedFlowCollection native "WebKitNamedFlowCollection" {
-
- @DomName('DOMNamedFlowCollection.length')
- @DocsEditable
- final int length;
-
- @DomName('DOMNamedFlowCollection.item')
- @DocsEditable
- NamedFlow item(int index) native;
-
- @DomName('DOMNamedFlowCollection.namedItem')
- @DocsEditable
- NamedFlow namedItem(String name) native;
-}
-// 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.
-
-
-@DocsEditable
@DomName('DOMParser')
class DomParser native "DOMParser" {
@@ -7031,507 +6807,6 @@
@DocsEditable
-@DomName('Path')
-class DomPath native "Path" {
-
- @DomName('DOMPath.DOMPath')
- @DocsEditable
- factory DomPath([path_OR_text]) {
- if (!?path_OR_text) {
- return DomPath._create_1();
- }
- if ((path_OR_text is DomPath || path_OR_text == null)) {
- return DomPath._create_2(path_OR_text);
- }
- if ((path_OR_text is String || path_OR_text == null)) {
- return DomPath._create_3(path_OR_text);
- }
- throw new ArgumentError("Incorrect number or type of arguments");
- }
- static DomPath _create_1() => JS('DomPath', 'new Path()');
- static DomPath _create_2(path_OR_text) => JS('DomPath', 'new Path(#)', path_OR_text);
- static DomPath _create_3(path_OR_text) => JS('DomPath', 'new Path(#)', path_OR_text);
-
- @DomName('DOMPath.arc')
- @DocsEditable
- void arc(num x, num y, num radius, num startAngle, num endAngle, bool anticlockwise) native;
-
- @DomName('DOMPath.arcTo')
- @DocsEditable
- void arcTo(num x1, num y1, num x2, num y2, num radius) native;
-
- @DomName('DOMPath.bezierCurveTo')
- @DocsEditable
- void bezierCurveTo(num cp1x, num cp1y, num cp2x, num cp2y, num x, num y) native;
-
- @DomName('DOMPath.closePath')
- @DocsEditable
- void closePath() native;
-
- @DomName('DOMPath.lineTo')
- @DocsEditable
- void lineTo(num x, num y) native;
-
- @DomName('DOMPath.moveTo')
- @DocsEditable
- void moveTo(num x, num y) native;
-
- @DomName('DOMPath.quadraticCurveTo')
- @DocsEditable
- void quadraticCurveTo(num cpx, num cpy, num x, num y) native;
-
- @DomName('DOMPath.rect')
- @DocsEditable
- void rect(num x, num y, num width, num height) native;
-}
-// 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.
-
-
-@DocsEditable
-@DomName('Plugin')
-class DomPlugin native "Plugin" {
-
- @DomName('DOMPlugin.description')
- @DocsEditable
- final String description;
-
- @DomName('DOMPlugin.filename')
- @DocsEditable
- final String filename;
-
- @DomName('DOMPlugin.length')
- @DocsEditable
- final int length;
-
- @DomName('DOMPlugin.name')
- @DocsEditable
- final String name;
-
- @DomName('DOMPlugin.item')
- @DocsEditable
- DomMimeType item(int index) native;
-
- @DomName('DOMPlugin.namedItem')
- @DocsEditable
- DomMimeType namedItem(String name) native;
-}
-// 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.
-
-
-@DocsEditable
-@DomName('PluginArray')
-class DomPluginArray implements JavaScriptIndexingBehavior, List<DomPlugin> native "PluginArray" {
-
- @DomName('DOMPluginArray.length')
- @DocsEditable
- int get length => JS("int", "#.length", this);
-
- DomPlugin operator[](int index) => JS("DomPlugin", "#[#]", this, index);
-
- void operator[]=(int index, DomPlugin value) {
- throw new UnsupportedError("Cannot assign element of immutable List.");
- }
- // -- start List<DomPlugin> mixins.
- // DomPlugin is the element type.
-
- // From Iterable<DomPlugin>:
-
- Iterator<DomPlugin> get iterator {
- // Note: NodeLists are not fixed size. And most probably length shouldn't
- // be cached in both iterator _and_ forEach method. For now caching it
- // for consistency.
- return new FixedSizeListIterator<DomPlugin>(this);
- }
-
- DomPlugin reduce(DomPlugin combine(DomPlugin value, DomPlugin element)) {
- return IterableMixinWorkaround.reduce(this, combine);
- }
-
- dynamic fold(dynamic initialValue,
- dynamic combine(dynamic previousValue, DomPlugin element)) {
- return IterableMixinWorkaround.fold(this, initialValue, combine);
- }
-
- bool contains(DomPlugin element) => IterableMixinWorkaround.contains(this, element);
-
- void forEach(void f(DomPlugin element)) => IterableMixinWorkaround.forEach(this, f);
-
- String join([String separator = ""]) =>
- IterableMixinWorkaround.joinList(this, separator);
-
- Iterable map(f(DomPlugin element)) =>
- IterableMixinWorkaround.mapList(this, f);
-
- Iterable<DomPlugin> where(bool f(DomPlugin element)) =>
- IterableMixinWorkaround.where(this, f);
-
- Iterable expand(Iterable f(DomPlugin element)) =>
- IterableMixinWorkaround.expand(this, f);
-
- bool every(bool f(DomPlugin element)) => IterableMixinWorkaround.every(this, f);
-
- bool any(bool f(DomPlugin element)) => IterableMixinWorkaround.any(this, f);
-
- List<DomPlugin> toList({ bool growable: true }) =>
- new List<DomPlugin>.from(this, growable: growable);
-
- Set<DomPlugin> toSet() => new Set<DomPlugin>.from(this);
-
- bool get isEmpty => this.length == 0;
-
- Iterable<DomPlugin> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
- Iterable<DomPlugin> takeWhile(bool test(DomPlugin value)) {
- return IterableMixinWorkaround.takeWhile(this, test);
- }
-
- Iterable<DomPlugin> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
- Iterable<DomPlugin> skipWhile(bool test(DomPlugin value)) {
- return IterableMixinWorkaround.skipWhile(this, test);
- }
-
- DomPlugin firstWhere(bool test(DomPlugin value), { DomPlugin orElse() }) {
- return IterableMixinWorkaround.firstWhere(this, test, orElse);
- }
-
- DomPlugin lastWhere(bool test(DomPlugin value), {DomPlugin orElse()}) {
- return IterableMixinWorkaround.lastWhereList(this, test, orElse);
- }
-
- DomPlugin singleWhere(bool test(DomPlugin value)) {
- return IterableMixinWorkaround.singleWhere(this, test);
- }
-
- DomPlugin elementAt(int index) {
- return this[index];
- }
-
- // From Collection<DomPlugin>:
-
- void add(DomPlugin value) {
- throw new UnsupportedError("Cannot add to immutable List.");
- }
-
- void addAll(Iterable<DomPlugin> iterable) {
- throw new UnsupportedError("Cannot add to immutable List.");
- }
-
- // From List<DomPlugin>:
- void set length(int value) {
- throw new UnsupportedError("Cannot resize immutable List.");
- }
-
- void clear() {
- throw new UnsupportedError("Cannot clear immutable List.");
- }
-
- Iterable<DomPlugin> get reversed {
- return IterableMixinWorkaround.reversedList(this);
- }
-
- void sort([int compare(DomPlugin a, DomPlugin b)]) {
- throw new UnsupportedError("Cannot sort immutable List.");
- }
-
- int indexOf(DomPlugin element, [int start = 0]) =>
- Lists.indexOf(this, element, start, this.length);
-
- int lastIndexOf(DomPlugin element, [int start]) {
- if (start == null) start = length - 1;
- return Lists.lastIndexOf(this, element, start);
- }
-
- DomPlugin get first {
- if (this.length > 0) return this[0];
- throw new StateError("No elements");
- }
-
- DomPlugin get last {
- if (this.length > 0) return this[this.length - 1];
- throw new StateError("No elements");
- }
-
- DomPlugin get single {
- if (length == 1) return this[0];
- if (length == 0) throw new StateError("No elements");
- throw new StateError("More than one element");
- }
-
- void insert(int index, DomPlugin element) {
- throw new UnsupportedError("Cannot add to immutable List.");
- }
-
- void insertAll(int index, Iterable<DomPlugin> iterable) {
- throw new UnsupportedError("Cannot add to immutable List.");
- }
-
- void setAll(int index, Iterable<DomPlugin> iterable) {
- throw new UnsupportedError("Cannot modify an immutable List.");
- }
-
- DomPlugin removeAt(int pos) {
- throw new UnsupportedError("Cannot remove from immutable List.");
- }
-
- DomPlugin removeLast() {
- throw new UnsupportedError("Cannot remove from immutable List.");
- }
-
- bool remove(Object object) {
- throw new UnsupportedError("Cannot remove from immutable List.");
- }
-
- void removeWhere(bool test(DomPlugin element)) {
- throw new UnsupportedError("Cannot remove from immutable List.");
- }
-
- void retainWhere(bool test(DomPlugin element)) {
- throw new UnsupportedError("Cannot remove from immutable List.");
- }
-
- void setRange(int start, int end, Iterable<DomPlugin> iterable, [int skipCount=0]) {
- throw new UnsupportedError("Cannot setRange on immutable List.");
- }
-
- void removeRange(int start, int end) {
- throw new UnsupportedError("Cannot removeRange on immutable List.");
- }
-
- void replaceRange(int start, int end, Iterable<DomPlugin> iterable) {
- throw new UnsupportedError("Cannot modify an immutable List.");
- }
-
- void fillRange(int start, int end, [DomPlugin fillValue]) {
- throw new UnsupportedError("Cannot modify an immutable List.");
- }
-
- Iterable<DomPlugin> getRange(int start, int end) =>
- IterableMixinWorkaround.getRangeList(this, start, end);
-
- List<DomPlugin> sublist(int start, [int end]) {
- if (end == null) end = length;
- return Lists.getRange(this, start, end, <DomPlugin>[]);
- }
-
- Map<int, DomPlugin> asMap() =>
- IterableMixinWorkaround.asMapList(this);
-
- String toString() {
- StringBuffer buffer = new StringBuffer('[');
- buffer.writeAll(this, ', ');
- buffer.write(']');
- return buffer.toString();
- }
-
- // -- end List<DomPlugin> mixins.
-
- @DomName('DOMPluginArray.item')
- @DocsEditable
- DomPlugin item(int index) native;
-
- @DomName('DOMPluginArray.namedItem')
- @DocsEditable
- DomPlugin namedItem(String name) native;
-
- @DomName('DOMPluginArray.refresh')
- @DocsEditable
- void refresh(bool reload) native;
-}
-// 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.
-
-
-@DocsEditable
-@DomName('SecurityPolicy')
-class DomSecurityPolicy native "SecurityPolicy" {
-
- @DomName('DOMSecurityPolicy.allowsEval')
- @DocsEditable
- final bool allowsEval;
-
- @DomName('DOMSecurityPolicy.allowsInlineScript')
- @DocsEditable
- final bool allowsInlineScript;
-
- @DomName('DOMSecurityPolicy.allowsInlineStyle')
- @DocsEditable
- final bool allowsInlineStyle;
-
- @DomName('DOMSecurityPolicy.isActive')
- @DocsEditable
- final bool isActive;
-
- @DomName('DOMSecurityPolicy.reportURIs')
- @DocsEditable
- @Returns('DomStringList')
- @Creates('DomStringList')
- final List<String> reportURIs;
-
- @DomName('DOMSecurityPolicy.allowsConnectionTo')
- @DocsEditable
- bool allowsConnectionTo(String url) native;
-
- @DomName('DOMSecurityPolicy.allowsFontFrom')
- @DocsEditable
- bool allowsFontFrom(String url) native;
-
- @DomName('DOMSecurityPolicy.allowsFormAction')
- @DocsEditable
- bool allowsFormAction(String url) native;
-
- @DomName('DOMSecurityPolicy.allowsFrameFrom')
- @DocsEditable
- bool allowsFrameFrom(String url) native;
-
- @DomName('DOMSecurityPolicy.allowsImageFrom')
- @DocsEditable
- bool allowsImageFrom(String url) native;
-
- @DomName('DOMSecurityPolicy.allowsMediaFrom')
- @DocsEditable
- bool allowsMediaFrom(String url) native;
-
- @DomName('DOMSecurityPolicy.allowsObjectFrom')
- @DocsEditable
- bool allowsObjectFrom(String url) native;
-
- @DomName('DOMSecurityPolicy.allowsPluginType')
- @DocsEditable
- bool allowsPluginType(String type) native;
-
- @DomName('DOMSecurityPolicy.allowsScriptFrom')
- @DocsEditable
- bool allowsScriptFrom(String url) native;
-
- @DomName('DOMSecurityPolicy.allowsStyleFrom')
- @DocsEditable
- bool allowsStyleFrom(String url) native;
-}
-// 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.
-
-
-@DocsEditable
-@DomName('Selection')
-class DomSelection native "Selection" {
-
- @DomName('DOMSelection.anchorNode')
- @DocsEditable
- final Node anchorNode;
-
- @DomName('DOMSelection.anchorOffset')
- @DocsEditable
- final int anchorOffset;
-
- @DomName('DOMSelection.baseNode')
- @DocsEditable
- final Node baseNode;
-
- @DomName('DOMSelection.baseOffset')
- @DocsEditable
- final int baseOffset;
-
- @DomName('DOMSelection.extentNode')
- @DocsEditable
- final Node extentNode;
-
- @DomName('DOMSelection.extentOffset')
- @DocsEditable
- final int extentOffset;
-
- @DomName('DOMSelection.focusNode')
- @DocsEditable
- final Node focusNode;
-
- @DomName('DOMSelection.focusOffset')
- @DocsEditable
- final int focusOffset;
-
- @DomName('DOMSelection.isCollapsed')
- @DocsEditable
- final bool isCollapsed;
-
- @DomName('DOMSelection.rangeCount')
- @DocsEditable
- final int rangeCount;
-
- @DomName('DOMSelection.type')
- @DocsEditable
- final String type;
-
- @DomName('DOMSelection.addRange')
- @DocsEditable
- void addRange(Range range) native;
-
- @DomName('DOMSelection.collapse')
- @DocsEditable
- void collapse(Node node, int index) native;
-
- @DomName('DOMSelection.collapseToEnd')
- @DocsEditable
- void collapseToEnd() native;
-
- @DomName('DOMSelection.collapseToStart')
- @DocsEditable
- void collapseToStart() native;
-
- @DomName('DOMSelection.containsNode')
- @DocsEditable
- bool containsNode(Node node, bool allowPartial) native;
-
- @DomName('DOMSelection.deleteFromDocument')
- @DocsEditable
- void deleteFromDocument() native;
-
- @DomName('DOMSelection.empty')
- @DocsEditable
- void empty() native;
-
- @DomName('DOMSelection.extend')
- @DocsEditable
- void extend(Node node, int offset) native;
-
- @DomName('DOMSelection.getRangeAt')
- @DocsEditable
- Range getRangeAt(int index) native;
-
- @DomName('DOMSelection.modify')
- @DocsEditable
- void modify(String alter, String direction, String granularity) native;
-
- @DomName('DOMSelection.removeAllRanges')
- @DocsEditable
- void removeAllRanges() native;
-
- @DomName('DOMSelection.selectAllChildren')
- @DocsEditable
- void selectAllChildren(Node node) native;
-
- @DomName('DOMSelection.setBaseAndExtent')
- @DocsEditable
- void setBaseAndExtent(Node baseNode, int baseOffset, Node extentNode, int extentOffset) native;
-
- @DomName('DOMSelection.setPosition')
- @DocsEditable
- void setPosition(Node node, int offset) native;
-
- @DomName('DOMSelection.toString')
- @DocsEditable
- String toString() native;
-}
-// 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.
-
-
-@DocsEditable
@DomName('DOMSettableTokenList')
class DomSettableTokenList extends DomTokenList native "DOMSettableTokenList" {
@@ -7935,11 +7210,32 @@
}
}
+/**
+ * An immutable list containing HTML elements. This list contains some
+ * additional methods for ease of CSS manipulation on a group of elements.
+ */
+abstract class ElementList<T extends Element> extends ListBase<T> {
+ /**
+ * The union of all CSS classes applied to the elements in this list.
+ *
+ * This set makes it easy to add, remove or toggle (add if not present, remove
+ * if present) the classes applied to a collection of elements.
+ *
+ * htmlList.classes.add('selected');
+ * htmlList.classes.toggle('isOnline');
+ * htmlList.classes.remove('selected');
+ */
+ CssClassSet get classes;
+
+ /** Replace the classes with `value` for every element in this list. */
+ set classes(Iterable<String> value);
+}
+
// TODO(jacobr): this is an inefficient implementation but it is hard to see
// a better option given that we cannot quite force NodeList to be an
// ElementList as there are valid cases where a NodeList JavaScript object
// contains Node objects that are not Elements.
-class _FrozenElementList<T extends Element> extends ListBase<T> {
+class _FrozenElementList<T extends Element> extends ListBase<T> implements ElementList {
final List<Node> _nodeList;
_FrozenElementList._wrap(this._nodeList);
@@ -7965,30 +7261,12 @@
Element get last => _nodeList.last;
Element get single => _nodeList.single;
-}
-class _ElementCssClassSet extends CssClassSet {
+ CssClassSet get classes => new _MultiElementCssClassSet(
+ _nodeList.where((e) => e is Element));
- final Element _element;
-
- _ElementCssClassSet(this._element);
-
- Set<String> readClasses() {
- var s = new LinkedHashSet<String>();
- var classname = _element.$dom_className;
-
- for (String name in classname.split(' ')) {
- String trimmed = name.trim();
- if (!trimmed.isEmpty) {
- s.add(trimmed);
- }
- }
- return s;
- }
-
- void writeClasses(Set<String> s) {
- List list = new List.from(s);
- _element.$dom_className = s.join(' ');
+ void set classes(Iterable<String> value) {
+ _nodeList.where((e) => e is Element).forEach((e) => e.classes = value);
}
}
@@ -8088,7 +7366,7 @@
*
* var items = element.query('.itemClassName');
*/
- List<Element> queryAll(String selectors) =>
+ ElementList queryAll(String selectors) =>
new _FrozenElementList._wrap($dom_querySelectorAll(selectors));
/**
@@ -13607,7 +12885,7 @@
@DocsEditable
void stop() native;
}
-// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
+// Copyright (c) 2013, 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.
@@ -13638,10 +12916,6 @@
@DocsEditable
String href;
- @DomName('Location.origin')
- @DocsEditable
- final String origin;
-
@DomName('Location.pathname')
@DocsEditable
String pathname;
@@ -13677,6 +12951,15 @@
@DomName('Location.valueOf')
@DocsEditable
Object valueOf() native;
+
+
+ @DomName('Location.origin')
+ String get origin {
+ if (JS('bool', '("origin" in #)', this)) {
+ return JS('String', '#.origin', this);
+ }
+ return '${this.protocol}//${this.host}';
+ }
}
// 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
@@ -14939,6 +14222,252 @@
@DocsEditable
+@DomName('MimeType')
+class MimeType native "MimeType" {
+
+ @DomName('DOMMimeType.description')
+ @DocsEditable
+ final String description;
+
+ @DomName('DOMMimeType.enabledPlugin')
+ @DocsEditable
+ final Plugin enabledPlugin;
+
+ @DomName('DOMMimeType.suffixes')
+ @DocsEditable
+ final String suffixes;
+
+ @DomName('DOMMimeType.type')
+ @DocsEditable
+ final String type;
+}
+// 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.
+
+
+@DocsEditable
+@DomName('MimeTypeArray')
+class MimeTypeArray implements JavaScriptIndexingBehavior, List<MimeType> native "MimeTypeArray" {
+
+ @DomName('DOMMimeTypeArray.length')
+ @DocsEditable
+ int get length => JS("int", "#.length", this);
+
+ MimeType operator[](int index) => JS("MimeType", "#[#]", this, index);
+
+ void operator[]=(int index, MimeType value) {
+ throw new UnsupportedError("Cannot assign element of immutable List.");
+ }
+ // -- start List<MimeType> mixins.
+ // MimeType is the element type.
+
+ // From Iterable<MimeType>:
+
+ Iterator<MimeType> get iterator {
+ // Note: NodeLists are not fixed size. And most probably length shouldn't
+ // be cached in both iterator _and_ forEach method. For now caching it
+ // for consistency.
+ return new FixedSizeListIterator<MimeType>(this);
+ }
+
+ MimeType reduce(MimeType combine(MimeType value, MimeType element)) {
+ return IterableMixinWorkaround.reduce(this, combine);
+ }
+
+ dynamic fold(dynamic initialValue,
+ dynamic combine(dynamic previousValue, MimeType element)) {
+ return IterableMixinWorkaround.fold(this, initialValue, combine);
+ }
+
+ bool contains(MimeType element) => IterableMixinWorkaround.contains(this, element);
+
+ void forEach(void f(MimeType element)) => IterableMixinWorkaround.forEach(this, f);
+
+ String join([String separator = ""]) =>
+ IterableMixinWorkaround.joinList(this, separator);
+
+ Iterable map(f(MimeType element)) =>
+ IterableMixinWorkaround.mapList(this, f);
+
+ Iterable<MimeType> where(bool f(MimeType element)) =>
+ IterableMixinWorkaround.where(this, f);
+
+ Iterable expand(Iterable f(MimeType element)) =>
+ IterableMixinWorkaround.expand(this, f);
+
+ bool every(bool f(MimeType element)) => IterableMixinWorkaround.every(this, f);
+
+ bool any(bool f(MimeType element)) => IterableMixinWorkaround.any(this, f);
+
+ List<MimeType> toList({ bool growable: true }) =>
+ new List<MimeType>.from(this, growable: growable);
+
+ Set<MimeType> toSet() => new Set<MimeType>.from(this);
+
+ bool get isEmpty => this.length == 0;
+
+ Iterable<MimeType> take(int n) => IterableMixinWorkaround.takeList(this, n);
+
+ Iterable<MimeType> takeWhile(bool test(MimeType value)) {
+ return IterableMixinWorkaround.takeWhile(this, test);
+ }
+
+ Iterable<MimeType> skip(int n) => IterableMixinWorkaround.skipList(this, n);
+
+ Iterable<MimeType> skipWhile(bool test(MimeType value)) {
+ return IterableMixinWorkaround.skipWhile(this, test);
+ }
+
+ MimeType firstWhere(bool test(MimeType value), { MimeType orElse() }) {
+ return IterableMixinWorkaround.firstWhere(this, test, orElse);
+ }
+
+ MimeType lastWhere(bool test(MimeType value), {MimeType orElse()}) {
+ return IterableMixinWorkaround.lastWhereList(this, test, orElse);
+ }
+
+ MimeType singleWhere(bool test(MimeType value)) {
+ return IterableMixinWorkaround.singleWhere(this, test);
+ }
+
+ MimeType elementAt(int index) {
+ return this[index];
+ }
+
+ // From Collection<MimeType>:
+
+ void add(MimeType value) {
+ throw new UnsupportedError("Cannot add to immutable List.");
+ }
+
+ void addAll(Iterable<MimeType> iterable) {
+ throw new UnsupportedError("Cannot add to immutable List.");
+ }
+
+ // From List<MimeType>:
+ void set length(int value) {
+ throw new UnsupportedError("Cannot resize immutable List.");
+ }
+
+ void clear() {
+ throw new UnsupportedError("Cannot clear immutable List.");
+ }
+
+ Iterable<MimeType> get reversed {
+ return IterableMixinWorkaround.reversedList(this);
+ }
+
+ void sort([int compare(MimeType a, MimeType b)]) {
+ throw new UnsupportedError("Cannot sort immutable List.");
+ }
+
+ int indexOf(MimeType element, [int start = 0]) =>
+ Lists.indexOf(this, element, start, this.length);
+
+ int lastIndexOf(MimeType element, [int start]) {
+ if (start == null) start = length - 1;
+ return Lists.lastIndexOf(this, element, start);
+ }
+
+ MimeType get first {
+ if (this.length > 0) return this[0];
+ throw new StateError("No elements");
+ }
+
+ MimeType get last {
+ if (this.length > 0) return this[this.length - 1];
+ throw new StateError("No elements");
+ }
+
+ MimeType get single {
+ if (length == 1) return this[0];
+ if (length == 0) throw new StateError("No elements");
+ throw new StateError("More than one element");
+ }
+
+ void insert(int index, MimeType element) {
+ throw new UnsupportedError("Cannot add to immutable List.");
+ }
+
+ void insertAll(int index, Iterable<MimeType> iterable) {
+ throw new UnsupportedError("Cannot add to immutable List.");
+ }
+
+ void setAll(int index, Iterable<MimeType> iterable) {
+ throw new UnsupportedError("Cannot modify an immutable List.");
+ }
+
+ MimeType removeAt(int pos) {
+ throw new UnsupportedError("Cannot remove from immutable List.");
+ }
+
+ MimeType removeLast() {
+ throw new UnsupportedError("Cannot remove from immutable List.");
+ }
+
+ bool remove(Object object) {
+ throw new UnsupportedError("Cannot remove from immutable List.");
+ }
+
+ void removeWhere(bool test(MimeType element)) {
+ throw new UnsupportedError("Cannot remove from immutable List.");
+ }
+
+ void retainWhere(bool test(MimeType element)) {
+ throw new UnsupportedError("Cannot remove from immutable List.");
+ }
+
+ void setRange(int start, int end, Iterable<MimeType> iterable, [int skipCount=0]) {
+ throw new UnsupportedError("Cannot setRange on immutable List.");
+ }
+
+ void removeRange(int start, int end) {
+ throw new UnsupportedError("Cannot removeRange on immutable List.");
+ }
+
+ void replaceRange(int start, int end, Iterable<MimeType> iterable) {
+ throw new UnsupportedError("Cannot modify an immutable List.");
+ }
+
+ void fillRange(int start, int end, [MimeType fillValue]) {
+ throw new UnsupportedError("Cannot modify an immutable List.");
+ }
+
+ Iterable<MimeType> getRange(int start, int end) =>
+ IterableMixinWorkaround.getRangeList(this, start, end);
+
+ List<MimeType> sublist(int start, [int end]) {
+ if (end == null) end = length;
+ return Lists.getRange(this, start, end, <MimeType>[]);
+ }
+
+ Map<int, MimeType> asMap() =>
+ IterableMixinWorkaround.asMapList(this);
+
+ String toString() {
+ StringBuffer buffer = new StringBuffer('[');
+ buffer.writeAll(this, ', ');
+ buffer.write(']');
+ return buffer.toString();
+ }
+
+ // -- end List<MimeType> mixins.
+
+ @DomName('DOMMimeTypeArray.item')
+ @DocsEditable
+ MimeType item(int index) native;
+
+ @DomName('DOMMimeTypeArray.namedItem')
+ @DocsEditable
+ MimeType namedItem(String name) native;
+}
+// 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.
+
+
+@DocsEditable
@DomName('HTMLModElement')
class ModElement extends Element native "HTMLModElement" {
@@ -15333,6 +14862,9 @@
@DocsEditable
@DomName('WebKitNamedFlow')
+@SupportedBrowser(SupportedBrowser.CHROME)
+@SupportedBrowser(SupportedBrowser.SAFARI)
+@Experimental
class NamedFlow extends EventTarget native "WebKitNamedFlow" {
@DomName('NamedFlow.firstEmptyRegionIndex')
@@ -15384,6 +14916,30 @@
// BSD-style license that can be found in the LICENSE file.
+@DocsEditable
+@DomName('WebKitNamedFlowCollection')
+@SupportedBrowser(SupportedBrowser.CHROME)
+@SupportedBrowser(SupportedBrowser.SAFARI)
+@Experimental
+class NamedFlowCollection native "WebKitNamedFlowCollection" {
+
+ @DomName('DOMNamedFlowCollection.length')
+ @DocsEditable
+ final int length;
+
+ @DomName('DOMNamedFlowCollection.item')
+ @DocsEditable
+ NamedFlow item(int index) native;
+
+ @DomName('DOMNamedFlowCollection.namedItem')
+ @DocsEditable
+ NamedFlow namedItem(String name) native;
+}
+// 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.
+
+
@DomName('Navigator')
class Navigator native "Navigator" {
@@ -15485,7 +15041,7 @@
@DomName('Navigator.mimeTypes')
@DocsEditable
- final DomMimeTypeArray mimeTypes;
+ final MimeTypeArray mimeTypes;
@DomName('Navigator.onLine')
@DocsEditable
@@ -15497,7 +15053,7 @@
@DomName('Navigator.plugins')
@DocsEditable
- final DomPluginArray plugins;
+ final PluginArray plugins;
@DomName('Navigator.product')
@DocsEditable
@@ -16864,6 +16420,65 @@
@DocsEditable
+@DomName('Path')
+class Path native "Path" {
+
+ @DomName('DOMPath.DOMPath')
+ @DocsEditable
+ factory Path([path_OR_text]) {
+ if (!?path_OR_text) {
+ return Path._create_1();
+ }
+ if ((path_OR_text is Path || path_OR_text == null)) {
+ return Path._create_2(path_OR_text);
+ }
+ if ((path_OR_text is String || path_OR_text == null)) {
+ return Path._create_3(path_OR_text);
+ }
+ throw new ArgumentError("Incorrect number or type of arguments");
+ }
+ static Path _create_1() => JS('Path', 'new Path()');
+ static Path _create_2(path_OR_text) => JS('Path', 'new Path(#)', path_OR_text);
+ static Path _create_3(path_OR_text) => JS('Path', 'new Path(#)', path_OR_text);
+
+ @DomName('DOMPath.arc')
+ @DocsEditable
+ void arc(num x, num y, num radius, num startAngle, num endAngle, bool anticlockwise) native;
+
+ @DomName('DOMPath.arcTo')
+ @DocsEditable
+ void arcTo(num x1, num y1, num x2, num y2, num radius) native;
+
+ @DomName('DOMPath.bezierCurveTo')
+ @DocsEditable
+ void bezierCurveTo(num cp1x, num cp1y, num cp2x, num cp2y, num x, num y) native;
+
+ @DomName('DOMPath.closePath')
+ @DocsEditable
+ void closePath() native;
+
+ @DomName('DOMPath.lineTo')
+ @DocsEditable
+ void lineTo(num x, num y) native;
+
+ @DomName('DOMPath.moveTo')
+ @DocsEditable
+ void moveTo(num x, num y) native;
+
+ @DomName('DOMPath.quadraticCurveTo')
+ @DocsEditable
+ void quadraticCurveTo(num cpx, num cpy, num x, num y) native;
+
+ @DomName('DOMPath.rect')
+ @DocsEditable
+ void rect(num x, num y, num width, num height) native;
+}
+// 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.
+
+
+@DocsEditable
@DomName('Performance')
@SupportedBrowser(SupportedBrowser.CHROME)
@SupportedBrowser(SupportedBrowser.FIREFOX)
@@ -17202,6 +16817,264 @@
@DocsEditable
+@DomName('Plugin')
+class Plugin native "Plugin" {
+
+ @DomName('DOMPlugin.description')
+ @DocsEditable
+ final String description;
+
+ @DomName('DOMPlugin.filename')
+ @DocsEditable
+ final String filename;
+
+ @DomName('DOMPlugin.length')
+ @DocsEditable
+ final int length;
+
+ @DomName('DOMPlugin.name')
+ @DocsEditable
+ final String name;
+
+ @DomName('DOMPlugin.item')
+ @DocsEditable
+ MimeType item(int index) native;
+
+ @DomName('DOMPlugin.namedItem')
+ @DocsEditable
+ MimeType namedItem(String name) native;
+}
+// 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.
+
+
+@DocsEditable
+@DomName('PluginArray')
+class PluginArray implements JavaScriptIndexingBehavior, List<Plugin> native "PluginArray" {
+
+ @DomName('DOMPluginArray.length')
+ @DocsEditable
+ int get length => JS("int", "#.length", this);
+
+ Plugin operator[](int index) => JS("Plugin", "#[#]", this, index);
+
+ void operator[]=(int index, Plugin value) {
+ throw new UnsupportedError("Cannot assign element of immutable List.");
+ }
+ // -- start List<Plugin> mixins.
+ // Plugin is the element type.
+
+ // From Iterable<Plugin>:
+
+ Iterator<Plugin> get iterator {
+ // Note: NodeLists are not fixed size. And most probably length shouldn't
+ // be cached in both iterator _and_ forEach method. For now caching it
+ // for consistency.
+ return new FixedSizeListIterator<Plugin>(this);
+ }
+
+ Plugin reduce(Plugin combine(Plugin value, Plugin element)) {
+ return IterableMixinWorkaround.reduce(this, combine);
+ }
+
+ dynamic fold(dynamic initialValue,
+ dynamic combine(dynamic previousValue, Plugin element)) {
+ return IterableMixinWorkaround.fold(this, initialValue, combine);
+ }
+
+ bool contains(Plugin element) => IterableMixinWorkaround.contains(this, element);
+
+ void forEach(void f(Plugin element)) => IterableMixinWorkaround.forEach(this, f);
+
+ String join([String separator = ""]) =>
+ IterableMixinWorkaround.joinList(this, separator);
+
+ Iterable map(f(Plugin element)) =>
+ IterableMixinWorkaround.mapList(this, f);
+
+ Iterable<Plugin> where(bool f(Plugin element)) =>
+ IterableMixinWorkaround.where(this, f);
+
+ Iterable expand(Iterable f(Plugin element)) =>
+ IterableMixinWorkaround.expand(this, f);
+
+ bool every(bool f(Plugin element)) => IterableMixinWorkaround.every(this, f);
+
+ bool any(bool f(Plugin element)) => IterableMixinWorkaround.any(this, f);
+
+ List<Plugin> toList({ bool growable: true }) =>
+ new List<Plugin>.from(this, growable: growable);
+
+ Set<Plugin> toSet() => new Set<Plugin>.from(this);
+
+ bool get isEmpty => this.length == 0;
+
+ Iterable<Plugin> take(int n) => IterableMixinWorkaround.takeList(this, n);
+
+ Iterable<Plugin> takeWhile(bool test(Plugin value)) {
+ return IterableMixinWorkaround.takeWhile(this, test);
+ }
+
+ Iterable<Plugin> skip(int n) => IterableMixinWorkaround.skipList(this, n);
+
+ Iterable<Plugin> skipWhile(bool test(Plugin value)) {
+ return IterableMixinWorkaround.skipWhile(this, test);
+ }
+
+ Plugin firstWhere(bool test(Plugin value), { Plugin orElse() }) {
+ return IterableMixinWorkaround.firstWhere(this, test, orElse);
+ }
+
+ Plugin lastWhere(bool test(Plugin value), {Plugin orElse()}) {
+ return IterableMixinWorkaround.lastWhereList(this, test, orElse);
+ }
+
+ Plugin singleWhere(bool test(Plugin value)) {
+ return IterableMixinWorkaround.singleWhere(this, test);
+ }
+
+ Plugin elementAt(int index) {
+ return this[index];
+ }
+
+ // From Collection<Plugin>:
+
+ void add(Plugin value) {
+ throw new UnsupportedError("Cannot add to immutable List.");
+ }
+
+ void addAll(Iterable<Plugin> iterable) {
+ throw new UnsupportedError("Cannot add to immutable List.");
+ }
+
+ // From List<Plugin>:
+ void set length(int value) {
+ throw new UnsupportedError("Cannot resize immutable List.");
+ }
+
+ void clear() {
+ throw new UnsupportedError("Cannot clear immutable List.");
+ }
+
+ Iterable<Plugin> get reversed {
+ return IterableMixinWorkaround.reversedList(this);
+ }
+
+ void sort([int compare(Plugin a, Plugin b)]) {
+ throw new UnsupportedError("Cannot sort immutable List.");
+ }
+
+ int indexOf(Plugin element, [int start = 0]) =>
+ Lists.indexOf(this, element, start, this.length);
+
+ int lastIndexOf(Plugin element, [int start]) {
+ if (start == null) start = length - 1;
+ return Lists.lastIndexOf(this, element, start);
+ }
+
+ Plugin get first {
+ if (this.length > 0) return this[0];
+ throw new StateError("No elements");
+ }
+
+ Plugin get last {
+ if (this.length > 0) return this[this.length - 1];
+ throw new StateError("No elements");
+ }
+
+ Plugin get single {
+ if (length == 1) return this[0];
+ if (length == 0) throw new StateError("No elements");
+ throw new StateError("More than one element");
+ }
+
+ void insert(int index, Plugin element) {
+ throw new UnsupportedError("Cannot add to immutable List.");
+ }
+
+ void insertAll(int index, Iterable<Plugin> iterable) {
+ throw new UnsupportedError("Cannot add to immutable List.");
+ }
+
+ void setAll(int index, Iterable<Plugin> iterable) {
+ throw new UnsupportedError("Cannot modify an immutable List.");
+ }
+
+ Plugin removeAt(int pos) {
+ throw new UnsupportedError("Cannot remove from immutable List.");
+ }
+
+ Plugin removeLast() {
+ throw new UnsupportedError("Cannot remove from immutable List.");
+ }
+
+ bool remove(Object object) {
+ throw new UnsupportedError("Cannot remove from immutable List.");
+ }
+
+ void removeWhere(bool test(Plugin element)) {
+ throw new UnsupportedError("Cannot remove from immutable List.");
+ }
+
+ void retainWhere(bool test(Plugin element)) {
+ throw new UnsupportedError("Cannot remove from immutable List.");
+ }
+
+ void setRange(int start, int end, Iterable<Plugin> iterable, [int skipCount=0]) {
+ throw new UnsupportedError("Cannot setRange on immutable List.");
+ }
+
+ void removeRange(int start, int end) {
+ throw new UnsupportedError("Cannot removeRange on immutable List.");
+ }
+
+ void replaceRange(int start, int end, Iterable<Plugin> iterable) {
+ throw new UnsupportedError("Cannot modify an immutable List.");
+ }
+
+ void fillRange(int start, int end, [Plugin fillValue]) {
+ throw new UnsupportedError("Cannot modify an immutable List.");
+ }
+
+ Iterable<Plugin> getRange(int start, int end) =>
+ IterableMixinWorkaround.getRangeList(this, start, end);
+
+ List<Plugin> sublist(int start, [int end]) {
+ if (end == null) end = length;
+ return Lists.getRange(this, start, end, <Plugin>[]);
+ }
+
+ Map<int, Plugin> asMap() =>
+ IterableMixinWorkaround.asMapList(this);
+
+ String toString() {
+ StringBuffer buffer = new StringBuffer('[');
+ buffer.writeAll(this, ', ');
+ buffer.write(']');
+ return buffer.toString();
+ }
+
+ // -- end List<Plugin> mixins.
+
+ @DomName('DOMPluginArray.item')
+ @DocsEditable
+ Plugin item(int index) native;
+
+ @DomName('DOMPluginArray.namedItem')
+ @DocsEditable
+ Plugin namedItem(String name) native;
+
+ @DomName('DOMPluginArray.refresh')
+ @DocsEditable
+ void refresh(bool reload) native;
+}
+// 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.
+
+
+@DocsEditable
@DomName('PopStateEvent')
@SupportedBrowser(SupportedBrowser.CHROME)
@SupportedBrowser(SupportedBrowser.FIREFOX)
@@ -17544,6 +17417,16 @@
@DocsEditable
String toString() native;
+
+ /**
+ * Checks if createContextualFragment is supported.
+ *
+ * See also:
+ *
+ * * [createContextualFragment]
+ */
+ static bool get supportsCreateContextualFragment =>
+ JS('bool', '("createContextualFragment" in window.Range.prototype)');
}
// 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
@@ -18364,6 +18247,77 @@
@DocsEditable
+@DomName('SecurityPolicy')
+class SecurityPolicy native "SecurityPolicy" {
+
+ @DomName('DOMSecurityPolicy.allowsEval')
+ @DocsEditable
+ final bool allowsEval;
+
+ @DomName('DOMSecurityPolicy.allowsInlineScript')
+ @DocsEditable
+ final bool allowsInlineScript;
+
+ @DomName('DOMSecurityPolicy.allowsInlineStyle')
+ @DocsEditable
+ final bool allowsInlineStyle;
+
+ @DomName('DOMSecurityPolicy.isActive')
+ @DocsEditable
+ final bool isActive;
+
+ @DomName('DOMSecurityPolicy.reportURIs')
+ @DocsEditable
+ @Returns('DomStringList')
+ @Creates('DomStringList')
+ final List<String> reportURIs;
+
+ @DomName('DOMSecurityPolicy.allowsConnectionTo')
+ @DocsEditable
+ bool allowsConnectionTo(String url) native;
+
+ @DomName('DOMSecurityPolicy.allowsFontFrom')
+ @DocsEditable
+ bool allowsFontFrom(String url) native;
+
+ @DomName('DOMSecurityPolicy.allowsFormAction')
+ @DocsEditable
+ bool allowsFormAction(String url) native;
+
+ @DomName('DOMSecurityPolicy.allowsFrameFrom')
+ @DocsEditable
+ bool allowsFrameFrom(String url) native;
+
+ @DomName('DOMSecurityPolicy.allowsImageFrom')
+ @DocsEditable
+ bool allowsImageFrom(String url) native;
+
+ @DomName('DOMSecurityPolicy.allowsMediaFrom')
+ @DocsEditable
+ bool allowsMediaFrom(String url) native;
+
+ @DomName('DOMSecurityPolicy.allowsObjectFrom')
+ @DocsEditable
+ bool allowsObjectFrom(String url) native;
+
+ @DomName('DOMSecurityPolicy.allowsPluginType')
+ @DocsEditable
+ bool allowsPluginType(String type) native;
+
+ @DomName('DOMSecurityPolicy.allowsScriptFrom')
+ @DocsEditable
+ bool allowsScriptFrom(String url) native;
+
+ @DomName('DOMSecurityPolicy.allowsStyleFrom')
+ @DocsEditable
+ bool allowsStyleFrom(String url) native;
+}
+// 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.
+
+
+@DocsEditable
@DomName('SecurityPolicyViolationEvent')
class SecurityPolicyViolationEvent extends Event native "SecurityPolicyViolationEvent" {
@@ -18515,6 +18469,119 @@
@DocsEditable
+@DomName('Selection')
+class Selection native "Selection" {
+
+ @DomName('DOMSelection.anchorNode')
+ @DocsEditable
+ final Node anchorNode;
+
+ @DomName('DOMSelection.anchorOffset')
+ @DocsEditable
+ final int anchorOffset;
+
+ @DomName('DOMSelection.baseNode')
+ @DocsEditable
+ final Node baseNode;
+
+ @DomName('DOMSelection.baseOffset')
+ @DocsEditable
+ final int baseOffset;
+
+ @DomName('DOMSelection.extentNode')
+ @DocsEditable
+ final Node extentNode;
+
+ @DomName('DOMSelection.extentOffset')
+ @DocsEditable
+ final int extentOffset;
+
+ @DomName('DOMSelection.focusNode')
+ @DocsEditable
+ final Node focusNode;
+
+ @DomName('DOMSelection.focusOffset')
+ @DocsEditable
+ final int focusOffset;
+
+ @DomName('DOMSelection.isCollapsed')
+ @DocsEditable
+ final bool isCollapsed;
+
+ @DomName('DOMSelection.rangeCount')
+ @DocsEditable
+ final int rangeCount;
+
+ @DomName('DOMSelection.type')
+ @DocsEditable
+ final String type;
+
+ @DomName('DOMSelection.addRange')
+ @DocsEditable
+ void addRange(Range range) native;
+
+ @DomName('DOMSelection.collapse')
+ @DocsEditable
+ void collapse(Node node, int index) native;
+
+ @DomName('DOMSelection.collapseToEnd')
+ @DocsEditable
+ void collapseToEnd() native;
+
+ @DomName('DOMSelection.collapseToStart')
+ @DocsEditable
+ void collapseToStart() native;
+
+ @DomName('DOMSelection.containsNode')
+ @DocsEditable
+ bool containsNode(Node node, bool allowPartial) native;
+
+ @DomName('DOMSelection.deleteFromDocument')
+ @DocsEditable
+ void deleteFromDocument() native;
+
+ @DomName('DOMSelection.empty')
+ @DocsEditable
+ void empty() native;
+
+ @DomName('DOMSelection.extend')
+ @DocsEditable
+ void extend(Node node, int offset) native;
+
+ @DomName('DOMSelection.getRangeAt')
+ @DocsEditable
+ Range getRangeAt(int index) native;
+
+ @DomName('DOMSelection.modify')
+ @DocsEditable
+ void modify(String alter, String direction, String granularity) native;
+
+ @DomName('DOMSelection.removeAllRanges')
+ @DocsEditable
+ void removeAllRanges() native;
+
+ @DomName('DOMSelection.selectAllChildren')
+ @DocsEditable
+ void selectAllChildren(Node node) native;
+
+ @DomName('DOMSelection.setBaseAndExtent')
+ @DocsEditable
+ void setBaseAndExtent(Node baseNode, int baseOffset, Node extentNode, int extentOffset) native;
+
+ @DomName('DOMSelection.setPosition')
+ @DocsEditable
+ void setPosition(Node node, int offset) native;
+
+ @DomName('DOMSelection.toString')
+ @DocsEditable
+ String toString() native;
+}
+// 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.
+
+
+@DocsEditable
@DomName('HTMLShadowElement')
@SupportedBrowser(SupportedBrowser.CHROME, '26')
@Experimental
@@ -18587,7 +18654,7 @@
@DomName('ShadowRoot.getSelection')
@DocsEditable
- DomSelection getSelection() native;
+ Selection getSelection() native;
static bool get supported =>
JS('bool', '!!(Element.prototype.webkitCreateShadowRoot)');
@@ -21724,34 +21791,6 @@
@DocsEditable
-@DomName('WebKitCSSFilterRule')
-class WebKitCssFilterRule extends CssRule native "WebKitCSSFilterRule" {
-
- @DomName('WebKitCSSFilterRule.style')
- @DocsEditable
- final CssStyleDeclaration style;
-}
-// 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.
-
-
-@DocsEditable
-@DomName('WebKitCSSRegionRule')
-class WebKitCssRegionRule extends CssRule native "WebKitCSSRegionRule" {
-
- @DomName('WebKitCSSRegionRule.cssRules')
- @DocsEditable
- @Returns('_CssRuleList')
- @Creates('_CssRuleList')
- final List<CssRule> cssRules;
-}
-// 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.
-
-
-@DocsEditable
/**
* Use the WebSocket interface to connect to a WebSocket,
* and to send and receive data on that WebSocket.
@@ -22725,7 +22764,7 @@
@DomName('DOMWindow.getSelection')
@DocsEditable
- DomSelection getSelection() native;
+ Selection getSelection() native;
@DomName('DOMWindow.matchMedia')
@DocsEditable
@@ -24281,6 +24320,9 @@
@SupportedBrowser(SupportedBrowser.CHROME)
@SupportedBrowser(SupportedBrowser.SAFARI)
@Experimental
+@SupportedBrowser(SupportedBrowser.CHROME)
+@SupportedBrowser(SupportedBrowser.SAFARI)
+@Experimental
class _DomPoint native "WebKitPoint" {
@DomName('DOMPoint.DOMPoint')
@@ -26490,78 +26532,28 @@
// BSD-style license that can be found in the LICENSE file.
+/** A Set that stores the CSS class names for an element. */
abstract class CssClassSet implements Set<String> {
- String toString() {
- return readClasses().join(' ');
- }
-
/**
* Adds the class [value] to the element if it is not on it, removes it if it
* is.
*/
- bool toggle(String value) {
- Set<String> s = readClasses();
- bool result = false;
- if (s.contains(value)) {
- s.remove(value);
- } else {
- s.add(value);
- result = true;
- }
- writeClasses(s);
- return result;
- }
+ bool toggle(String value);
/**
* Returns [:true:] if classes cannot be added or removed from this
* [:CssClassSet:].
*/
- bool get frozen => false;
+ bool get frozen;
- // interface Iterable - BEGIN
- Iterator<String> get iterator => readClasses().iterator;
- // interface Iterable - END
-
- // interface Collection - BEGIN
- void forEach(void f(String element)) {
- readClasses().forEach(f);
- }
-
- String join([String separator = ""]) => readClasses().join(separator);
-
- Iterable map(f(String element)) => readClasses().map(f);
-
- Iterable<String> where(bool f(String element)) => readClasses().where(f);
-
- Iterable expand(Iterable f(String element)) => readClasses().expand(f);
-
- bool every(bool f(String element)) => readClasses().every(f);
-
- bool any(bool f(String element)) => readClasses().any(f);
-
- bool get isEmpty => readClasses().isEmpty;
-
- int get length => readClasses().length;
-
- String reduce(String combine(String value, String element)) {
- return readClasses().reduce(combine);
- }
-
- dynamic fold(dynamic initialValue,
- dynamic combine(dynamic previousValue, String element)) {
- return readClasses().fold(initialValue, combine);
- }
- // interface Collection - END
-
- // interface Set - BEGIN
/**
* Determine if this element contains the class [value].
*
* This is the Dart equivalent of jQuery's
* [hasClass](http://api.jquery.com/hasClass/).
*/
- bool contains(String value) => readClasses().contains(value);
+ bool contains(String value);
/**
* Add the class [value] to element.
@@ -26569,11 +26561,7 @@
* This is the Dart equivalent of jQuery's
* [addClass](http://api.jquery.com/addClass/).
*/
- void add(String value) {
- // TODO - figure out if we need to do any validation here
- // or if the browser natively does enough.
- _modify((s) => s.add(value));
- }
+ void add(String value);
/**
* Remove the class [value] from element, and return true on successful
@@ -26582,13 +26570,7 @@
* This is the Dart equivalent of jQuery's
* [removeClass](http://api.jquery.com/removeClass/).
*/
- bool remove(Object value) {
- if (value is! String) return false;
- Set<String> s = readClasses();
- bool result = s.remove(value);
- writeClasses(s);
- return result;
- }
+ bool remove(Object value);
/**
* Add all classes specified in [iterable] to element.
@@ -26596,10 +26578,7 @@
* This is the Dart equivalent of jQuery's
* [addClass](http://api.jquery.com/addClass/).
*/
- void addAll(Iterable<String> iterable) {
- // TODO - see comment above about validation.
- _modify((s) => s.addAll(iterable));
- }
+ void addAll(Iterable<String> iterable);
/**
* Remove all classes specified in [iterable] from element.
@@ -26607,9 +26586,7 @@
* This is the Dart equivalent of jQuery's
* [removeClass](http://api.jquery.com/removeClass/).
*/
- void removeAll(Iterable<String> iterable) {
- _modify((s) => s.removeAll(iterable));
- }
+ void removeAll(Iterable<String> iterable);
/**
* Toggles all classes specified in [iterable] on element.
@@ -26618,59 +26595,35 @@
* remove it if it is. This is the Dart equivalent of jQuery's
* [toggleClass](http://api.jquery.com/toggleClass/).
*/
- void toggleAll(Iterable<String> iterable) {
- iterable.forEach(toggle);
+ void toggleAll(Iterable<String> iterable);
+}
+
+/**
+ * A set (union) of the CSS classes that are present in a set of elements.
+ * Implemented separately from _ElementCssClassSet for performance.
+ */
+class _MultiElementCssClassSet extends CssClassSetImpl {
+ final Iterable<Element> _elementIterable;
+ Iterable<_ElementCssClassSet> _elementCssClassSetIterable;
+
+ _MultiElementCssClassSet(this._elementIterable) {
+ _elementCssClassSetIterable = new List.from(_elementIterable).map(
+ (e) => new _ElementCssClassSet(e));
}
- void retainAll(Iterable<String> iterable) {
- _modify((s) => s.retainAll(iterable));
+ Set<String> readClasses() {
+ var s = new LinkedHashSet<String>();
+ _elementCssClassSetIterable.forEach((e) => s.addAll(e.readClasses()));
+ return s;
}
- void removeWhere(bool test(String name)) {
- _modify((s) => s.removeWhere(test));
+ void writeClasses(Set<String> s) {
+ var classes = new List.from(s).join(' ');
+ for (Element e in _elementIterable) {
+ e.$dom_className = classes;
+ }
}
- void retainWhere(bool test(String name)) {
- _modify((s) => s.retainWhere(test));
- }
-
- bool containsAll(Iterable<String> collection) =>
- readClasses().containsAll(collection);
-
- Set<String> intersection(Set<String> other) =>
- readClasses().intersection(other);
-
- Set<String> union(Set<String> other) =>
- readClasses().union(other);
-
- Set<String> difference(Set<String> other) =>
- readClasses().difference(other);
-
- String get first => readClasses().first;
- String get last => readClasses().last;
- String get single => readClasses().single;
- List<String> toList({ bool growable: true }) =>
- readClasses().toList(growable: growable);
- Set<String> toSet() => readClasses().toSet();
- Iterable<String> take(int n) => readClasses().take(n);
- Iterable<String> takeWhile(bool test(String value)) =>
- readClasses().takeWhile(test);
- Iterable<String> skip(int n) => readClasses().skip(n);
- Iterable<String> skipWhile(bool test(String value)) =>
- readClasses().skipWhile(test);
- String firstWhere(bool test(String value), { String orElse() }) =>
- readClasses().firstWhere(test, orElse: orElse);
- String lastWhere(bool test(String value), {String orElse()}) =>
- readClasses().lastWhere(test, orElse: orElse);
- String singleWhere(bool test(String value)) =>
- readClasses().singleWhere(test);
- String elementAt(int index) => readClasses().elementAt(index);
-
- void clear() {
- _modify((s) => s.clear());
- }
- // interface Set - END
-
/**
* Helper method used to modify the set of css classes on this element.
*
@@ -26680,25 +26633,53 @@
* After f returns, the modified set is written to the
* className property of this element.
*/
- void _modify( f(Set<String> s)) {
- Set<String> s = readClasses();
- f(s);
- writeClasses(s);
+ void modify( f(Set<String> s)) {
+ _elementCssClassSetIterable.forEach((e) => e.modify(f));
}
/**
- * Read the class names from the Element class property,
- * and put them into a set (duplicates are discarded).
- * This is intended to be overridden by specific implementations.
+ * Adds the class [value] to the element if it is not on it, removes it if it
+ * is.
*/
- Set<String> readClasses();
+ bool toggle(String value) =>
+ _modifyWithReturnValue((e) => e.toggle(value));
/**
- * Join all the elements of a set into one string and write
- * back to the element.
- * This is intended to be overridden by specific implementations.
+ * Remove the class [value] from element, and return true on successful
+ * removal.
+ *
+ * This is the Dart equivalent of jQuery's
+ * [removeClass](http://api.jquery.com/removeClass/).
*/
- void writeClasses(Set<String> s);
+ bool remove(Object value) => _modifyWithReturnValue((e) => e.remove(value));
+
+ bool _modifyWithReturnValue(f) => _elementCssClassSetIterable.fold(
+ false, (prevValue, element) => f(element) || prevValue);
+}
+
+class _ElementCssClassSet extends CssClassSetImpl {
+
+ final Element _element;
+
+ _ElementCssClassSet(this._element);
+
+ Set<String> readClasses() {
+ var s = new LinkedHashSet<String>();
+ var classname = _element.$dom_className;
+
+ for (String name in classname.split(' ')) {
+ String trimmed = name.trim();
+ if (!trimmed.isEmpty) {
+ s.add(trimmed);
+ }
+ }
+ return s;
+ }
+
+ void writeClasses(Set<String> s) {
+ List list = new List.from(s);
+ _element.$dom_className = s.join(' ');
+ }
}
// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
@@ -29469,7 +29450,12 @@
}
// final String origin;
- String get origin => _get(_ptr, 'origin');
+ String get origin {
+ if (JS('bool', '("origin" in #)', _ptr)) {
+ return JS('String', '#.origin', _ptr);
+ }
+ return '${this.protocol}//${this.host}';
+ }
// String pathname;
String get pathname => _get(_ptr, 'pathname');
diff --git a/sdk/lib/html/dartium/html_dartium.dart b/sdk/lib/html/dartium/html_dartium.dart
index 40efe9a..3b32ec2 100644
--- a/sdk/lib/html/dartium/html_dartium.dart
+++ b/sdk/lib/html/dartium/html_dartium.dart
@@ -262,6 +262,9 @@
@DocsEditable
@DomName('WebKitAnimationEvent')
+@SupportedBrowser(SupportedBrowser.CHROME)
+@SupportedBrowser(SupportedBrowser.SAFARI)
+@Experimental
class AnimationEvent extends Event {
AnimationEvent.internal() : super.internal();
@@ -1189,11 +1192,11 @@
@DomName('CanvasRenderingContext2D.currentPath')
@DocsEditable
- DomPath get currentPath native "CanvasRenderingContext2D_currentPath_Getter";
+ Path get currentPath native "CanvasRenderingContext2D_currentPath_Getter";
@DomName('CanvasRenderingContext2D.currentPath')
@DocsEditable
- void set currentPath(DomPath value) native "CanvasRenderingContext2D_currentPath_Setter";
+ void set currentPath(Path value) native "CanvasRenderingContext2D_currentPath_Setter";
@DomName('CanvasRenderingContext2D.fillStyle')
@DocsEditable
@@ -2208,6 +2211,26 @@
@DocsEditable
+@DomName('WebKitCSSFilterRule')
+@SupportedBrowser(SupportedBrowser.CHROME)
+@SupportedBrowser(SupportedBrowser.SAFARI)
+@Experimental
+class CssFilterRule extends CssRule {
+ CssFilterRule.internal() : super.internal();
+
+ @DomName('WebKitCSSFilterRule.style')
+ @DocsEditable
+ CssStyleDeclaration get style native "WebKitCSSFilterRule_style_Getter";
+
+}
+// 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.
+
+// WARNING: Do not edit - generated code.
+
+
+@DocsEditable
@DomName('CSSFontFaceLoadEvent')
class CssFontFaceLoadEvent extends Event {
CssFontFaceLoadEvent.internal() : super.internal();
@@ -2299,6 +2322,9 @@
@DocsEditable
@DomName('WebKitCSSKeyframeRule')
+@SupportedBrowser(SupportedBrowser.CHROME)
+@SupportedBrowser(SupportedBrowser.SAFARI)
+@Experimental
class CssKeyframeRule extends CssRule {
CssKeyframeRule.internal() : super.internal();
@@ -2324,6 +2350,9 @@
@DocsEditable
@DomName('WebKitCSSKeyframesRule')
+@SupportedBrowser(SupportedBrowser.CHROME)
+@SupportedBrowser(SupportedBrowser.SAFARI)
+@Experimental
class CssKeyframesRule extends CssRule {
CssKeyframesRule.internal() : super.internal();
@@ -2414,6 +2443,26 @@
@DocsEditable
+@DomName('WebKitCSSRegionRule')
+@SupportedBrowser(SupportedBrowser.CHROME)
+@SupportedBrowser(SupportedBrowser.SAFARI)
+@Experimental
+class CssRegionRule extends CssRule {
+ CssRegionRule.internal() : super.internal();
+
+ @DomName('WebKitCSSRegionRule.cssRules')
+ @DocsEditable
+ List<CssRule> get cssRules native "WebKitCSSRegionRule_cssRules_Getter";
+
+}
+// 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.
+
+// WARNING: Do not edit - generated code.
+
+
+@DocsEditable
@DomName('CSSRule')
class CssRule extends NativeFieldWrapperClass1 {
CssRule.internal();
@@ -6474,7 +6523,7 @@
@DomName('Document.securityPolicy')
@DocsEditable
- DomSecurityPolicy get securityPolicy native "Document_securityPolicy_Getter";
+ SecurityPolicy get securityPolicy native "Document_securityPolicy_Getter";
@DomName('Document.selectedStylesheetSet')
@DocsEditable
@@ -6718,7 +6767,7 @@
@SupportedBrowser(SupportedBrowser.CHROME)
@SupportedBrowser(SupportedBrowser.SAFARI)
@Experimental
- DomNamedFlowCollection getNamedFlows() native "Document_webkitGetNamedFlows_Callback";
+ NamedFlowCollection getNamedFlows() native "Document_webkitGetNamedFlows_Callback";
@DomName('Document.webkitRegister')
@DocsEditable
@@ -6938,7 +6987,7 @@
* For details about CSS selector syntax, see the
* [CSS selector specification](http://www.w3.org/TR/css3-selectors/).
*/
- List<Element> queryAll(String selectors) {
+ ElementList queryAll(String selectors) {
return new _FrozenElementList._wrap($dom_querySelectorAll(selectors));
}
}
@@ -7142,285 +7191,6 @@
@DocsEditable
-@DomName('MimeType')
-class DomMimeType extends NativeFieldWrapperClass1 {
- DomMimeType.internal();
-
- @DomName('DOMMimeType.description')
- @DocsEditable
- String get description native "DOMMimeType_description_Getter";
-
- @DomName('DOMMimeType.enabledPlugin')
- @DocsEditable
- DomPlugin get enabledPlugin native "DOMMimeType_enabledPlugin_Getter";
-
- @DomName('DOMMimeType.suffixes')
- @DocsEditable
- String get suffixes native "DOMMimeType_suffixes_Getter";
-
- @DomName('DOMMimeType.type')
- @DocsEditable
- String get type native "DOMMimeType_type_Getter";
-
-}
-// 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.
-
-// WARNING: Do not edit - generated code.
-
-
-@DocsEditable
-@DomName('MimeTypeArray')
-class DomMimeTypeArray extends NativeFieldWrapperClass1 implements List<DomMimeType> {
- DomMimeTypeArray.internal();
-
- @DomName('DOMMimeTypeArray.length')
- @DocsEditable
- int get length native "DOMMimeTypeArray_length_Getter";
-
- DomMimeType operator[](int index) native "DOMMimeTypeArray_item_Callback";
-
- void operator[]=(int index, DomMimeType value) {
- throw new UnsupportedError("Cannot assign element of immutable List.");
- }
- // -- start List<DomMimeType> mixins.
- // DomMimeType is the element type.
-
- // From Iterable<DomMimeType>:
-
- Iterator<DomMimeType> get iterator {
- // Note: NodeLists are not fixed size. And most probably length shouldn't
- // be cached in both iterator _and_ forEach method. For now caching it
- // for consistency.
- return new FixedSizeListIterator<DomMimeType>(this);
- }
-
- DomMimeType reduce(DomMimeType combine(DomMimeType value, DomMimeType element)) {
- return IterableMixinWorkaround.reduce(this, combine);
- }
-
- dynamic fold(dynamic initialValue,
- dynamic combine(dynamic previousValue, DomMimeType element)) {
- return IterableMixinWorkaround.fold(this, initialValue, combine);
- }
-
- bool contains(DomMimeType element) => IterableMixinWorkaround.contains(this, element);
-
- void forEach(void f(DomMimeType element)) => IterableMixinWorkaround.forEach(this, f);
-
- String join([String separator = ""]) =>
- IterableMixinWorkaround.joinList(this, separator);
-
- Iterable map(f(DomMimeType element)) =>
- IterableMixinWorkaround.mapList(this, f);
-
- Iterable<DomMimeType> where(bool f(DomMimeType element)) =>
- IterableMixinWorkaround.where(this, f);
-
- Iterable expand(Iterable f(DomMimeType element)) =>
- IterableMixinWorkaround.expand(this, f);
-
- bool every(bool f(DomMimeType element)) => IterableMixinWorkaround.every(this, f);
-
- bool any(bool f(DomMimeType element)) => IterableMixinWorkaround.any(this, f);
-
- List<DomMimeType> toList({ bool growable: true }) =>
- new List<DomMimeType>.from(this, growable: growable);
-
- Set<DomMimeType> toSet() => new Set<DomMimeType>.from(this);
-
- bool get isEmpty => this.length == 0;
-
- Iterable<DomMimeType> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
- Iterable<DomMimeType> takeWhile(bool test(DomMimeType value)) {
- return IterableMixinWorkaround.takeWhile(this, test);
- }
-
- Iterable<DomMimeType> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
- Iterable<DomMimeType> skipWhile(bool test(DomMimeType value)) {
- return IterableMixinWorkaround.skipWhile(this, test);
- }
-
- DomMimeType firstWhere(bool test(DomMimeType value), { DomMimeType orElse() }) {
- return IterableMixinWorkaround.firstWhere(this, test, orElse);
- }
-
- DomMimeType lastWhere(bool test(DomMimeType value), {DomMimeType orElse()}) {
- return IterableMixinWorkaround.lastWhereList(this, test, orElse);
- }
-
- DomMimeType singleWhere(bool test(DomMimeType value)) {
- return IterableMixinWorkaround.singleWhere(this, test);
- }
-
- DomMimeType elementAt(int index) {
- return this[index];
- }
-
- // From Collection<DomMimeType>:
-
- void add(DomMimeType value) {
- throw new UnsupportedError("Cannot add to immutable List.");
- }
-
- void addAll(Iterable<DomMimeType> iterable) {
- throw new UnsupportedError("Cannot add to immutable List.");
- }
-
- // From List<DomMimeType>:
- void set length(int value) {
- throw new UnsupportedError("Cannot resize immutable List.");
- }
-
- void clear() {
- throw new UnsupportedError("Cannot clear immutable List.");
- }
-
- Iterable<DomMimeType> get reversed {
- return IterableMixinWorkaround.reversedList(this);
- }
-
- void sort([int compare(DomMimeType a, DomMimeType b)]) {
- throw new UnsupportedError("Cannot sort immutable List.");
- }
-
- int indexOf(DomMimeType element, [int start = 0]) =>
- Lists.indexOf(this, element, start, this.length);
-
- int lastIndexOf(DomMimeType element, [int start]) {
- if (start == null) start = length - 1;
- return Lists.lastIndexOf(this, element, start);
- }
-
- DomMimeType get first {
- if (this.length > 0) return this[0];
- throw new StateError("No elements");
- }
-
- DomMimeType get last {
- if (this.length > 0) return this[this.length - 1];
- throw new StateError("No elements");
- }
-
- DomMimeType get single {
- if (length == 1) return this[0];
- if (length == 0) throw new StateError("No elements");
- throw new StateError("More than one element");
- }
-
- void insert(int index, DomMimeType element) {
- throw new UnsupportedError("Cannot add to immutable List.");
- }
-
- void insertAll(int index, Iterable<DomMimeType> iterable) {
- throw new UnsupportedError("Cannot add to immutable List.");
- }
-
- void setAll(int index, Iterable<DomMimeType> iterable) {
- throw new UnsupportedError("Cannot modify an immutable List.");
- }
-
- DomMimeType removeAt(int pos) {
- throw new UnsupportedError("Cannot remove from immutable List.");
- }
-
- DomMimeType removeLast() {
- throw new UnsupportedError("Cannot remove from immutable List.");
- }
-
- bool remove(Object object) {
- throw new UnsupportedError("Cannot remove from immutable List.");
- }
-
- void removeWhere(bool test(DomMimeType element)) {
- throw new UnsupportedError("Cannot remove from immutable List.");
- }
-
- void retainWhere(bool test(DomMimeType element)) {
- throw new UnsupportedError("Cannot remove from immutable List.");
- }
-
- void setRange(int start, int end, Iterable<DomMimeType> iterable, [int skipCount=0]) {
- throw new UnsupportedError("Cannot setRange on immutable List.");
- }
-
- void removeRange(int start, int end) {
- throw new UnsupportedError("Cannot removeRange on immutable List.");
- }
-
- void replaceRange(int start, int end, Iterable<DomMimeType> iterable) {
- throw new UnsupportedError("Cannot modify an immutable List.");
- }
-
- void fillRange(int start, int end, [DomMimeType fillValue]) {
- throw new UnsupportedError("Cannot modify an immutable List.");
- }
-
- Iterable<DomMimeType> getRange(int start, int end) =>
- IterableMixinWorkaround.getRangeList(this, start, end);
-
- List<DomMimeType> sublist(int start, [int end]) {
- if (end == null) end = length;
- return Lists.getRange(this, start, end, <DomMimeType>[]);
- }
-
- Map<int, DomMimeType> asMap() =>
- IterableMixinWorkaround.asMapList(this);
-
- String toString() {
- StringBuffer buffer = new StringBuffer('[');
- buffer.writeAll(this, ', ');
- buffer.write(']');
- return buffer.toString();
- }
-
- // -- end List<DomMimeType> mixins.
-
- @DomName('DOMMimeTypeArray.item')
- @DocsEditable
- DomMimeType item(int index) native "DOMMimeTypeArray_item_Callback";
-
- @DomName('DOMMimeTypeArray.namedItem')
- @DocsEditable
- DomMimeType namedItem(String name) native "DOMMimeTypeArray_namedItem_Callback";
-
-}
-// 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.
-
-// WARNING: Do not edit - generated code.
-
-
-@DocsEditable
-@DomName('WebKitNamedFlowCollection')
-class DomNamedFlowCollection extends NativeFieldWrapperClass1 {
- DomNamedFlowCollection.internal();
-
- @DomName('DOMNamedFlowCollection.length')
- @DocsEditable
- int get length native "DOMNamedFlowCollection_length_Getter";
-
- @DomName('DOMNamedFlowCollection.item')
- @DocsEditable
- NamedFlow item(int index) native "DOMNamedFlowCollection_item_Callback";
-
- @DomName('DOMNamedFlowCollection.namedItem')
- @DocsEditable
- NamedFlow namedItem(String name) native "DOMNamedFlowCollection_namedItem_Callback";
-
-}
-// 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.
-
-// WARNING: Do not edit - generated code.
-
-
-@DocsEditable
@DomName('DOMParser')
class DomParser extends NativeFieldWrapperClass1 {
DomParser.internal();
@@ -7447,531 +7217,6 @@
@DocsEditable
-@DomName('Path')
-class DomPath extends NativeFieldWrapperClass1 {
- DomPath.internal();
-
- @DomName('DOMPath.DOMPath')
- @DocsEditable
- factory DomPath([path_OR_text]) {
- if (!?path_OR_text) {
- return DomPath._create_1();
- }
- if ((path_OR_text is DomPath || path_OR_text == null)) {
- return DomPath._create_2(path_OR_text);
- }
- if ((path_OR_text is String || path_OR_text == null)) {
- return DomPath._create_3(path_OR_text);
- }
- throw new ArgumentError("Incorrect number or type of arguments");
- }
-
- @DocsEditable
- static DomPath _create_1() native "DOMPath__create_1constructorCallback";
-
- @DocsEditable
- static DomPath _create_2(path_OR_text) native "DOMPath__create_2constructorCallback";
-
- @DocsEditable
- static DomPath _create_3(path_OR_text) native "DOMPath__create_3constructorCallback";
-
- @DomName('DOMPath.arc')
- @DocsEditable
- void arc(num x, num y, num radius, num startAngle, num endAngle, bool anticlockwise) native "DOMPath_arc_Callback";
-
- @DomName('DOMPath.arcTo')
- @DocsEditable
- void arcTo(num x1, num y1, num x2, num y2, num radius) native "DOMPath_arcTo_Callback";
-
- @DomName('DOMPath.bezierCurveTo')
- @DocsEditable
- void bezierCurveTo(num cp1x, num cp1y, num cp2x, num cp2y, num x, num y) native "DOMPath_bezierCurveTo_Callback";
-
- @DomName('DOMPath.closePath')
- @DocsEditable
- void closePath() native "DOMPath_closePath_Callback";
-
- @DomName('DOMPath.lineTo')
- @DocsEditable
- void lineTo(num x, num y) native "DOMPath_lineTo_Callback";
-
- @DomName('DOMPath.moveTo')
- @DocsEditable
- void moveTo(num x, num y) native "DOMPath_moveTo_Callback";
-
- @DomName('DOMPath.quadraticCurveTo')
- @DocsEditable
- void quadraticCurveTo(num cpx, num cpy, num x, num y) native "DOMPath_quadraticCurveTo_Callback";
-
- @DomName('DOMPath.rect')
- @DocsEditable
- void rect(num x, num y, num width, num height) native "DOMPath_rect_Callback";
-
-}
-// 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.
-
-// WARNING: Do not edit - generated code.
-
-
-@DocsEditable
-@DomName('Plugin')
-class DomPlugin extends NativeFieldWrapperClass1 {
- DomPlugin.internal();
-
- @DomName('DOMPlugin.description')
- @DocsEditable
- String get description native "DOMPlugin_description_Getter";
-
- @DomName('DOMPlugin.filename')
- @DocsEditable
- String get filename native "DOMPlugin_filename_Getter";
-
- @DomName('DOMPlugin.length')
- @DocsEditable
- int get length native "DOMPlugin_length_Getter";
-
- @DomName('DOMPlugin.name')
- @DocsEditable
- String get name native "DOMPlugin_name_Getter";
-
- @DomName('DOMPlugin.item')
- @DocsEditable
- DomMimeType item(int index) native "DOMPlugin_item_Callback";
-
- @DomName('DOMPlugin.namedItem')
- @DocsEditable
- DomMimeType namedItem(String name) native "DOMPlugin_namedItem_Callback";
-
-}
-// 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.
-
-// WARNING: Do not edit - generated code.
-
-
-@DocsEditable
-@DomName('PluginArray')
-class DomPluginArray extends NativeFieldWrapperClass1 implements List<DomPlugin> {
- DomPluginArray.internal();
-
- @DomName('DOMPluginArray.length')
- @DocsEditable
- int get length native "DOMPluginArray_length_Getter";
-
- DomPlugin operator[](int index) native "DOMPluginArray_item_Callback";
-
- void operator[]=(int index, DomPlugin value) {
- throw new UnsupportedError("Cannot assign element of immutable List.");
- }
- // -- start List<DomPlugin> mixins.
- // DomPlugin is the element type.
-
- // From Iterable<DomPlugin>:
-
- Iterator<DomPlugin> get iterator {
- // Note: NodeLists are not fixed size. And most probably length shouldn't
- // be cached in both iterator _and_ forEach method. For now caching it
- // for consistency.
- return new FixedSizeListIterator<DomPlugin>(this);
- }
-
- DomPlugin reduce(DomPlugin combine(DomPlugin value, DomPlugin element)) {
- return IterableMixinWorkaround.reduce(this, combine);
- }
-
- dynamic fold(dynamic initialValue,
- dynamic combine(dynamic previousValue, DomPlugin element)) {
- return IterableMixinWorkaround.fold(this, initialValue, combine);
- }
-
- bool contains(DomPlugin element) => IterableMixinWorkaround.contains(this, element);
-
- void forEach(void f(DomPlugin element)) => IterableMixinWorkaround.forEach(this, f);
-
- String join([String separator = ""]) =>
- IterableMixinWorkaround.joinList(this, separator);
-
- Iterable map(f(DomPlugin element)) =>
- IterableMixinWorkaround.mapList(this, f);
-
- Iterable<DomPlugin> where(bool f(DomPlugin element)) =>
- IterableMixinWorkaround.where(this, f);
-
- Iterable expand(Iterable f(DomPlugin element)) =>
- IterableMixinWorkaround.expand(this, f);
-
- bool every(bool f(DomPlugin element)) => IterableMixinWorkaround.every(this, f);
-
- bool any(bool f(DomPlugin element)) => IterableMixinWorkaround.any(this, f);
-
- List<DomPlugin> toList({ bool growable: true }) =>
- new List<DomPlugin>.from(this, growable: growable);
-
- Set<DomPlugin> toSet() => new Set<DomPlugin>.from(this);
-
- bool get isEmpty => this.length == 0;
-
- Iterable<DomPlugin> take(int n) => IterableMixinWorkaround.takeList(this, n);
-
- Iterable<DomPlugin> takeWhile(bool test(DomPlugin value)) {
- return IterableMixinWorkaround.takeWhile(this, test);
- }
-
- Iterable<DomPlugin> skip(int n) => IterableMixinWorkaround.skipList(this, n);
-
- Iterable<DomPlugin> skipWhile(bool test(DomPlugin value)) {
- return IterableMixinWorkaround.skipWhile(this, test);
- }
-
- DomPlugin firstWhere(bool test(DomPlugin value), { DomPlugin orElse() }) {
- return IterableMixinWorkaround.firstWhere(this, test, orElse);
- }
-
- DomPlugin lastWhere(bool test(DomPlugin value), {DomPlugin orElse()}) {
- return IterableMixinWorkaround.lastWhereList(this, test, orElse);
- }
-
- DomPlugin singleWhere(bool test(DomPlugin value)) {
- return IterableMixinWorkaround.singleWhere(this, test);
- }
-
- DomPlugin elementAt(int index) {
- return this[index];
- }
-
- // From Collection<DomPlugin>:
-
- void add(DomPlugin value) {
- throw new UnsupportedError("Cannot add to immutable List.");
- }
-
- void addAll(Iterable<DomPlugin> iterable) {
- throw new UnsupportedError("Cannot add to immutable List.");
- }
-
- // From List<DomPlugin>:
- void set length(int value) {
- throw new UnsupportedError("Cannot resize immutable List.");
- }
-
- void clear() {
- throw new UnsupportedError("Cannot clear immutable List.");
- }
-
- Iterable<DomPlugin> get reversed {
- return IterableMixinWorkaround.reversedList(this);
- }
-
- void sort([int compare(DomPlugin a, DomPlugin b)]) {
- throw new UnsupportedError("Cannot sort immutable List.");
- }
-
- int indexOf(DomPlugin element, [int start = 0]) =>
- Lists.indexOf(this, element, start, this.length);
-
- int lastIndexOf(DomPlugin element, [int start]) {
- if (start == null) start = length - 1;
- return Lists.lastIndexOf(this, element, start);
- }
-
- DomPlugin get first {
- if (this.length > 0) return this[0];
- throw new StateError("No elements");
- }
-
- DomPlugin get last {
- if (this.length > 0) return this[this.length - 1];
- throw new StateError("No elements");
- }
-
- DomPlugin get single {
- if (length == 1) return this[0];
- if (length == 0) throw new StateError("No elements");
- throw new StateError("More than one element");
- }
-
- void insert(int index, DomPlugin element) {
- throw new UnsupportedError("Cannot add to immutable List.");
- }
-
- void insertAll(int index, Iterable<DomPlugin> iterable) {
- throw new UnsupportedError("Cannot add to immutable List.");
- }
-
- void setAll(int index, Iterable<DomPlugin> iterable) {
- throw new UnsupportedError("Cannot modify an immutable List.");
- }
-
- DomPlugin removeAt(int pos) {
- throw new UnsupportedError("Cannot remove from immutable List.");
- }
-
- DomPlugin removeLast() {
- throw new UnsupportedError("Cannot remove from immutable List.");
- }
-
- bool remove(Object object) {
- throw new UnsupportedError("Cannot remove from immutable List.");
- }
-
- void removeWhere(bool test(DomPlugin element)) {
- throw new UnsupportedError("Cannot remove from immutable List.");
- }
-
- void retainWhere(bool test(DomPlugin element)) {
- throw new UnsupportedError("Cannot remove from immutable List.");
- }
-
- void setRange(int start, int end, Iterable<DomPlugin> iterable, [int skipCount=0]) {
- throw new UnsupportedError("Cannot setRange on immutable List.");
- }
-
- void removeRange(int start, int end) {
- throw new UnsupportedError("Cannot removeRange on immutable List.");
- }
-
- void replaceRange(int start, int end, Iterable<DomPlugin> iterable) {
- throw new UnsupportedError("Cannot modify an immutable List.");
- }
-
- void fillRange(int start, int end, [DomPlugin fillValue]) {
- throw new UnsupportedError("Cannot modify an immutable List.");
- }
-
- Iterable<DomPlugin> getRange(int start, int end) =>
- IterableMixinWorkaround.getRangeList(this, start, end);
-
- List<DomPlugin> sublist(int start, [int end]) {
- if (end == null) end = length;
- return Lists.getRange(this, start, end, <DomPlugin>[]);
- }
-
- Map<int, DomPlugin> asMap() =>
- IterableMixinWorkaround.asMapList(this);
-
- String toString() {
- StringBuffer buffer = new StringBuffer('[');
- buffer.writeAll(this, ', ');
- buffer.write(']');
- return buffer.toString();
- }
-
- // -- end List<DomPlugin> mixins.
-
- @DomName('DOMPluginArray.item')
- @DocsEditable
- DomPlugin item(int index) native "DOMPluginArray_item_Callback";
-
- @DomName('DOMPluginArray.namedItem')
- @DocsEditable
- DomPlugin namedItem(String name) native "DOMPluginArray_namedItem_Callback";
-
- @DomName('DOMPluginArray.refresh')
- @DocsEditable
- void refresh(bool reload) native "DOMPluginArray_refresh_Callback";
-
-}
-// 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.
-
-// WARNING: Do not edit - generated code.
-
-
-@DocsEditable
-@DomName('SecurityPolicy')
-class DomSecurityPolicy extends NativeFieldWrapperClass1 {
- DomSecurityPolicy.internal();
-
- @DomName('DOMSecurityPolicy.allowsEval')
- @DocsEditable
- bool get allowsEval native "DOMSecurityPolicy_allowsEval_Getter";
-
- @DomName('DOMSecurityPolicy.allowsInlineScript')
- @DocsEditable
- bool get allowsInlineScript native "DOMSecurityPolicy_allowsInlineScript_Getter";
-
- @DomName('DOMSecurityPolicy.allowsInlineStyle')
- @DocsEditable
- bool get allowsInlineStyle native "DOMSecurityPolicy_allowsInlineStyle_Getter";
-
- @DomName('DOMSecurityPolicy.isActive')
- @DocsEditable
- bool get isActive native "DOMSecurityPolicy_isActive_Getter";
-
- @DomName('DOMSecurityPolicy.reportURIs')
- @DocsEditable
- List<String> get reportURIs native "DOMSecurityPolicy_reportURIs_Getter";
-
- @DomName('DOMSecurityPolicy.allowsConnectionTo')
- @DocsEditable
- bool allowsConnectionTo(String url) native "DOMSecurityPolicy_allowsConnectionTo_Callback";
-
- @DomName('DOMSecurityPolicy.allowsFontFrom')
- @DocsEditable
- bool allowsFontFrom(String url) native "DOMSecurityPolicy_allowsFontFrom_Callback";
-
- @DomName('DOMSecurityPolicy.allowsFormAction')
- @DocsEditable
- bool allowsFormAction(String url) native "DOMSecurityPolicy_allowsFormAction_Callback";
-
- @DomName('DOMSecurityPolicy.allowsFrameFrom')
- @DocsEditable
- bool allowsFrameFrom(String url) native "DOMSecurityPolicy_allowsFrameFrom_Callback";
-
- @DomName('DOMSecurityPolicy.allowsImageFrom')
- @DocsEditable
- bool allowsImageFrom(String url) native "DOMSecurityPolicy_allowsImageFrom_Callback";
-
- @DomName('DOMSecurityPolicy.allowsMediaFrom')
- @DocsEditable
- bool allowsMediaFrom(String url) native "DOMSecurityPolicy_allowsMediaFrom_Callback";
-
- @DomName('DOMSecurityPolicy.allowsObjectFrom')
- @DocsEditable
- bool allowsObjectFrom(String url) native "DOMSecurityPolicy_allowsObjectFrom_Callback";
-
- @DomName('DOMSecurityPolicy.allowsPluginType')
- @DocsEditable
- bool allowsPluginType(String type) native "DOMSecurityPolicy_allowsPluginType_Callback";
-
- @DomName('DOMSecurityPolicy.allowsScriptFrom')
- @DocsEditable
- bool allowsScriptFrom(String url) native "DOMSecurityPolicy_allowsScriptFrom_Callback";
-
- @DomName('DOMSecurityPolicy.allowsStyleFrom')
- @DocsEditable
- bool allowsStyleFrom(String url) native "DOMSecurityPolicy_allowsStyleFrom_Callback";
-
-}
-// 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.
-
-// WARNING: Do not edit - generated code.
-
-
-@DocsEditable
-@DomName('Selection')
-class DomSelection extends NativeFieldWrapperClass1 {
- DomSelection.internal();
-
- @DomName('DOMSelection.anchorNode')
- @DocsEditable
- Node get anchorNode native "DOMSelection_anchorNode_Getter";
-
- @DomName('DOMSelection.anchorOffset')
- @DocsEditable
- int get anchorOffset native "DOMSelection_anchorOffset_Getter";
-
- @DomName('DOMSelection.baseNode')
- @DocsEditable
- Node get baseNode native "DOMSelection_baseNode_Getter";
-
- @DomName('DOMSelection.baseOffset')
- @DocsEditable
- int get baseOffset native "DOMSelection_baseOffset_Getter";
-
- @DomName('DOMSelection.extentNode')
- @DocsEditable
- Node get extentNode native "DOMSelection_extentNode_Getter";
-
- @DomName('DOMSelection.extentOffset')
- @DocsEditable
- int get extentOffset native "DOMSelection_extentOffset_Getter";
-
- @DomName('DOMSelection.focusNode')
- @DocsEditable
- Node get focusNode native "DOMSelection_focusNode_Getter";
-
- @DomName('DOMSelection.focusOffset')
- @DocsEditable
- int get focusOffset native "DOMSelection_focusOffset_Getter";
-
- @DomName('DOMSelection.isCollapsed')
- @DocsEditable
- bool get isCollapsed native "DOMSelection_isCollapsed_Getter";
-
- @DomName('DOMSelection.rangeCount')
- @DocsEditable
- int get rangeCount native "DOMSelection_rangeCount_Getter";
-
- @DomName('DOMSelection.type')
- @DocsEditable
- String get type native "DOMSelection_type_Getter";
-
- @DomName('DOMSelection.addRange')
- @DocsEditable
- void addRange(Range range) native "DOMSelection_addRange_Callback";
-
- @DomName('DOMSelection.collapse')
- @DocsEditable
- void collapse(Node node, int index) native "DOMSelection_collapse_Callback";
-
- @DomName('DOMSelection.collapseToEnd')
- @DocsEditable
- void collapseToEnd() native "DOMSelection_collapseToEnd_Callback";
-
- @DomName('DOMSelection.collapseToStart')
- @DocsEditable
- void collapseToStart() native "DOMSelection_collapseToStart_Callback";
-
- @DomName('DOMSelection.containsNode')
- @DocsEditable
- bool containsNode(Node node, bool allowPartial) native "DOMSelection_containsNode_Callback";
-
- @DomName('DOMSelection.deleteFromDocument')
- @DocsEditable
- void deleteFromDocument() native "DOMSelection_deleteFromDocument_Callback";
-
- @DomName('DOMSelection.empty')
- @DocsEditable
- void empty() native "DOMSelection_empty_Callback";
-
- @DomName('DOMSelection.extend')
- @DocsEditable
- void extend(Node node, int offset) native "DOMSelection_extend_Callback";
-
- @DomName('DOMSelection.getRangeAt')
- @DocsEditable
- Range getRangeAt(int index) native "DOMSelection_getRangeAt_Callback";
-
- @DomName('DOMSelection.modify')
- @DocsEditable
- void modify(String alter, String direction, String granularity) native "DOMSelection_modify_Callback";
-
- @DomName('DOMSelection.removeAllRanges')
- @DocsEditable
- void removeAllRanges() native "DOMSelection_removeAllRanges_Callback";
-
- @DomName('DOMSelection.selectAllChildren')
- @DocsEditable
- void selectAllChildren(Node node) native "DOMSelection_selectAllChildren_Callback";
-
- @DomName('DOMSelection.setBaseAndExtent')
- @DocsEditable
- void setBaseAndExtent(Node baseNode, int baseOffset, Node extentNode, int extentOffset) native "DOMSelection_setBaseAndExtent_Callback";
-
- @DomName('DOMSelection.setPosition')
- @DocsEditable
- void setPosition(Node node, int offset) native "DOMSelection_setPosition_Callback";
-
- @DomName('DOMSelection.toString')
- @DocsEditable
- String toString() native "DOMSelection_toString_Callback";
-
-}
-// 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.
-
-// WARNING: Do not edit - generated code.
-
-
-@DocsEditable
@DomName('DOMSettableTokenList')
class DomSettableTokenList extends DomTokenList {
DomSettableTokenList.internal() : super.internal();
@@ -8401,11 +7646,32 @@
}
}
+/**
+ * An immutable list containing HTML elements. This list contains some
+ * additional methods for ease of CSS manipulation on a group of elements.
+ */
+abstract class ElementList<T extends Element> extends ListBase<T> {
+ /**
+ * The union of all CSS classes applied to the elements in this list.
+ *
+ * This set makes it easy to add, remove or toggle (add if not present, remove
+ * if present) the classes applied to a collection of elements.
+ *
+ * htmlList.classes.add('selected');
+ * htmlList.classes.toggle('isOnline');
+ * htmlList.classes.remove('selected');
+ */
+ CssClassSet get classes;
+
+ /** Replace the classes with `value` for every element in this list. */
+ set classes(Iterable<String> value);
+}
+
// TODO(jacobr): this is an inefficient implementation but it is hard to see
// a better option given that we cannot quite force NodeList to be an
// ElementList as there are valid cases where a NodeList JavaScript object
// contains Node objects that are not Elements.
-class _FrozenElementList<T extends Element> extends ListBase<T> {
+class _FrozenElementList<T extends Element> extends ListBase<T> implements ElementList {
final List<Node> _nodeList;
_FrozenElementList._wrap(this._nodeList);
@@ -8431,30 +7697,12 @@
Element get last => _nodeList.last;
Element get single => _nodeList.single;
-}
-class _ElementCssClassSet extends CssClassSet {
+ CssClassSet get classes => new _MultiElementCssClassSet(
+ _nodeList.where((e) => e is Element));
- final Element _element;
-
- _ElementCssClassSet(this._element);
-
- Set<String> readClasses() {
- var s = new LinkedHashSet<String>();
- var classname = _element.$dom_className;
-
- for (String name in classname.split(' ')) {
- String trimmed = name.trim();
- if (!trimmed.isEmpty) {
- s.add(trimmed);
- }
- }
- return s;
- }
-
- void writeClasses(Set<String> s) {
- List list = new List.from(s);
- _element.$dom_className = s.join(' ');
+ void set classes(Iterable<String> value) {
+ _nodeList.where((e) => e is Element).forEach((e) => e.classes = value);
}
}
@@ -8554,7 +7802,7 @@
*
* var items = element.query('.itemClassName');
*/
- List<Element> queryAll(String selectors) =>
+ ElementList queryAll(String selectors) =>
new _FrozenElementList._wrap($dom_querySelectorAll(selectors));
/**
@@ -14394,12 +13642,10 @@
void stop() native "LocalMediaStream_stop_Callback";
}
-// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
+// Copyright (c) 2013, 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.
-// WARNING: Do not edit - generated code.
-
@DocsEditable
@DomName('Location')
@@ -14498,6 +13744,7 @@
@DocsEditable
Object valueOf() native "Location_valueOf_Callback";
+
}
// 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
@@ -15956,6 +15203,260 @@
@DocsEditable
+@DomName('MimeType')
+class MimeType extends NativeFieldWrapperClass1 {
+ MimeType.internal();
+
+ @DomName('DOMMimeType.description')
+ @DocsEditable
+ String get description native "DOMMimeType_description_Getter";
+
+ @DomName('DOMMimeType.enabledPlugin')
+ @DocsEditable
+ Plugin get enabledPlugin native "DOMMimeType_enabledPlugin_Getter";
+
+ @DomName('DOMMimeType.suffixes')
+ @DocsEditable
+ String get suffixes native "DOMMimeType_suffixes_Getter";
+
+ @DomName('DOMMimeType.type')
+ @DocsEditable
+ String get type native "DOMMimeType_type_Getter";
+
+}
+// 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.
+
+// WARNING: Do not edit - generated code.
+
+
+@DocsEditable
+@DomName('MimeTypeArray')
+class MimeTypeArray extends NativeFieldWrapperClass1 implements List<MimeType> {
+ MimeTypeArray.internal();
+
+ @DomName('DOMMimeTypeArray.length')
+ @DocsEditable
+ int get length native "DOMMimeTypeArray_length_Getter";
+
+ MimeType operator[](int index) native "DOMMimeTypeArray_item_Callback";
+
+ void operator[]=(int index, MimeType value) {
+ throw new UnsupportedError("Cannot assign element of immutable List.");
+ }
+ // -- start List<MimeType> mixins.
+ // MimeType is the element type.
+
+ // From Iterable<MimeType>:
+
+ Iterator<MimeType> get iterator {
+ // Note: NodeLists are not fixed size. And most probably length shouldn't
+ // be cached in both iterator _and_ forEach method. For now caching it
+ // for consistency.
+ return new FixedSizeListIterator<MimeType>(this);
+ }
+
+ MimeType reduce(MimeType combine(MimeType value, MimeType element)) {
+ return IterableMixinWorkaround.reduce(this, combine);
+ }
+
+ dynamic fold(dynamic initialValue,
+ dynamic combine(dynamic previousValue, MimeType element)) {
+ return IterableMixinWorkaround.fold(this, initialValue, combine);
+ }
+
+ bool contains(MimeType element) => IterableMixinWorkaround.contains(this, element);
+
+ void forEach(void f(MimeType element)) => IterableMixinWorkaround.forEach(this, f);
+
+ String join([String separator = ""]) =>
+ IterableMixinWorkaround.joinList(this, separator);
+
+ Iterable map(f(MimeType element)) =>
+ IterableMixinWorkaround.mapList(this, f);
+
+ Iterable<MimeType> where(bool f(MimeType element)) =>
+ IterableMixinWorkaround.where(this, f);
+
+ Iterable expand(Iterable f(MimeType element)) =>
+ IterableMixinWorkaround.expand(this, f);
+
+ bool every(bool f(MimeType element)) => IterableMixinWorkaround.every(this, f);
+
+ bool any(bool f(MimeType element)) => IterableMixinWorkaround.any(this, f);
+
+ List<MimeType> toList({ bool growable: true }) =>
+ new List<MimeType>.from(this, growable: growable);
+
+ Set<MimeType> toSet() => new Set<MimeType>.from(this);
+
+ bool get isEmpty => this.length == 0;
+
+ Iterable<MimeType> take(int n) => IterableMixinWorkaround.takeList(this, n);
+
+ Iterable<MimeType> takeWhile(bool test(MimeType value)) {
+ return IterableMixinWorkaround.takeWhile(this, test);
+ }
+
+ Iterable<MimeType> skip(int n) => IterableMixinWorkaround.skipList(this, n);
+
+ Iterable<MimeType> skipWhile(bool test(MimeType value)) {
+ return IterableMixinWorkaround.skipWhile(this, test);
+ }
+
+ MimeType firstWhere(bool test(MimeType value), { MimeType orElse() }) {
+ return IterableMixinWorkaround.firstWhere(this, test, orElse);
+ }
+
+ MimeType lastWhere(bool test(MimeType value), {MimeType orElse()}) {
+ return IterableMixinWorkaround.lastWhereList(this, test, orElse);
+ }
+
+ MimeType singleWhere(bool test(MimeType value)) {
+ return IterableMixinWorkaround.singleWhere(this, test);
+ }
+
+ MimeType elementAt(int index) {
+ return this[index];
+ }
+
+ // From Collection<MimeType>:
+
+ void add(MimeType value) {
+ throw new UnsupportedError("Cannot add to immutable List.");
+ }
+
+ void addAll(Iterable<MimeType> iterable) {
+ throw new UnsupportedError("Cannot add to immutable List.");
+ }
+
+ // From List<MimeType>:
+ void set length(int value) {
+ throw new UnsupportedError("Cannot resize immutable List.");
+ }
+
+ void clear() {
+ throw new UnsupportedError("Cannot clear immutable List.");
+ }
+
+ Iterable<MimeType> get reversed {
+ return IterableMixinWorkaround.reversedList(this);
+ }
+
+ void sort([int compare(MimeType a, MimeType b)]) {
+ throw new UnsupportedError("Cannot sort immutable List.");
+ }
+
+ int indexOf(MimeType element, [int start = 0]) =>
+ Lists.indexOf(this, element, start, this.length);
+
+ int lastIndexOf(MimeType element, [int start]) {
+ if (start == null) start = length - 1;
+ return Lists.lastIndexOf(this, element, start);
+ }
+
+ MimeType get first {
+ if (this.length > 0) return this[0];
+ throw new StateError("No elements");
+ }
+
+ MimeType get last {
+ if (this.length > 0) return this[this.length - 1];
+ throw new StateError("No elements");
+ }
+
+ MimeType get single {
+ if (length == 1) return this[0];
+ if (length == 0) throw new StateError("No elements");
+ throw new StateError("More than one element");
+ }
+
+ void insert(int index, MimeType element) {
+ throw new UnsupportedError("Cannot add to immutable List.");
+ }
+
+ void insertAll(int index, Iterable<MimeType> iterable) {
+ throw new UnsupportedError("Cannot add to immutable List.");
+ }
+
+ void setAll(int index, Iterable<MimeType> iterable) {
+ throw new UnsupportedError("Cannot modify an immutable List.");
+ }
+
+ MimeType removeAt(int pos) {
+ throw new UnsupportedError("Cannot remove from immutable List.");
+ }
+
+ MimeType removeLast() {
+ throw new UnsupportedError("Cannot remove from immutable List.");
+ }
+
+ bool remove(Object object) {
+ throw new UnsupportedError("Cannot remove from immutable List.");
+ }
+
+ void removeWhere(bool test(MimeType element)) {
+ throw new UnsupportedError("Cannot remove from immutable List.");
+ }
+
+ void retainWhere(bool test(MimeType element)) {
+ throw new UnsupportedError("Cannot remove from immutable List.");
+ }
+
+ void setRange(int start, int end, Iterable<MimeType> iterable, [int skipCount=0]) {
+ throw new UnsupportedError("Cannot setRange on immutable List.");
+ }
+
+ void removeRange(int start, int end) {
+ throw new UnsupportedError("Cannot removeRange on immutable List.");
+ }
+
+ void replaceRange(int start, int end, Iterable<MimeType> iterable) {
+ throw new UnsupportedError("Cannot modify an immutable List.");
+ }
+
+ void fillRange(int start, int end, [MimeType fillValue]) {
+ throw new UnsupportedError("Cannot modify an immutable List.");
+ }
+
+ Iterable<MimeType> getRange(int start, int end) =>
+ IterableMixinWorkaround.getRangeList(this, start, end);
+
+ List<MimeType> sublist(int start, [int end]) {
+ if (end == null) end = length;
+ return Lists.getRange(this, start, end, <MimeType>[]);
+ }
+
+ Map<int, MimeType> asMap() =>
+ IterableMixinWorkaround.asMapList(this);
+
+ String toString() {
+ StringBuffer buffer = new StringBuffer('[');
+ buffer.writeAll(this, ', ');
+ buffer.write(']');
+ return buffer.toString();
+ }
+
+ // -- end List<MimeType> mixins.
+
+ @DomName('DOMMimeTypeArray.item')
+ @DocsEditable
+ MimeType item(int index) native "DOMMimeTypeArray_item_Callback";
+
+ @DomName('DOMMimeTypeArray.namedItem')
+ @DocsEditable
+ MimeType namedItem(String name) native "DOMMimeTypeArray_namedItem_Callback";
+
+}
+// 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.
+
+// WARNING: Do not edit - generated code.
+
+
+@DocsEditable
@DomName('HTMLModElement')
class ModElement extends _Element_Merged {
ModElement.internal() : super.internal();
@@ -16326,6 +15827,9 @@
@DocsEditable
@DomName('WebKitNamedFlow')
+@SupportedBrowser(SupportedBrowser.CHROME)
+@SupportedBrowser(SupportedBrowser.SAFARI)
+@Experimental
class NamedFlow extends EventTarget {
NamedFlow.internal() : super.internal();
@@ -16370,6 +15874,34 @@
// 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.
+// WARNING: Do not edit - generated code.
+
+
+@DocsEditable
+@DomName('WebKitNamedFlowCollection')
+@SupportedBrowser(SupportedBrowser.CHROME)
+@SupportedBrowser(SupportedBrowser.SAFARI)
+@Experimental
+class NamedFlowCollection extends NativeFieldWrapperClass1 {
+ NamedFlowCollection.internal();
+
+ @DomName('DOMNamedFlowCollection.length')
+ @DocsEditable
+ int get length native "DOMNamedFlowCollection_length_Getter";
+
+ @DomName('DOMNamedFlowCollection.item')
+ @DocsEditable
+ NamedFlow item(int index) native "DOMNamedFlowCollection_item_Callback";
+
+ @DomName('DOMNamedFlowCollection.namedItem')
+ @DocsEditable
+ NamedFlow namedItem(String name) native "DOMNamedFlowCollection_namedItem_Callback";
+
+}
+// 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.
+
@DomName('Navigator')
class Navigator extends NativeFieldWrapperClass1 {
@@ -16462,7 +15994,7 @@
@DomName('Navigator.mimeTypes')
@DocsEditable
- DomMimeTypeArray get mimeTypes native "Navigator_mimeTypes_Getter";
+ MimeTypeArray get mimeTypes native "Navigator_mimeTypes_Getter";
@DomName('Navigator.onLine')
@DocsEditable
@@ -16474,7 +16006,7 @@
@DomName('Navigator.plugins')
@DocsEditable
- DomPluginArray get plugins native "Navigator_plugins_Getter";
+ PluginArray get plugins native "Navigator_plugins_Getter";
@DomName('Navigator.product')
@DocsEditable
@@ -17957,6 +17489,75 @@
@DocsEditable
+@DomName('Path')
+class Path extends NativeFieldWrapperClass1 {
+ Path.internal();
+
+ @DomName('DOMPath.DOMPath')
+ @DocsEditable
+ factory Path([path_OR_text]) {
+ if (!?path_OR_text) {
+ return Path._create_1();
+ }
+ if ((path_OR_text is Path || path_OR_text == null)) {
+ return Path._create_2(path_OR_text);
+ }
+ if ((path_OR_text is String || path_OR_text == null)) {
+ return Path._create_3(path_OR_text);
+ }
+ throw new ArgumentError("Incorrect number or type of arguments");
+ }
+
+ @DocsEditable
+ static Path _create_1() native "DOMPath__create_1constructorCallback";
+
+ @DocsEditable
+ static Path _create_2(path_OR_text) native "DOMPath__create_2constructorCallback";
+
+ @DocsEditable
+ static Path _create_3(path_OR_text) native "DOMPath__create_3constructorCallback";
+
+ @DomName('DOMPath.arc')
+ @DocsEditable
+ void arc(num x, num y, num radius, num startAngle, num endAngle, bool anticlockwise) native "DOMPath_arc_Callback";
+
+ @DomName('DOMPath.arcTo')
+ @DocsEditable
+ void arcTo(num x1, num y1, num x2, num y2, num radius) native "DOMPath_arcTo_Callback";
+
+ @DomName('DOMPath.bezierCurveTo')
+ @DocsEditable
+ void bezierCurveTo(num cp1x, num cp1y, num cp2x, num cp2y, num x, num y) native "DOMPath_bezierCurveTo_Callback";
+
+ @DomName('DOMPath.closePath')
+ @DocsEditable
+ void closePath() native "DOMPath_closePath_Callback";
+
+ @DomName('DOMPath.lineTo')
+ @DocsEditable
+ void lineTo(num x, num y) native "DOMPath_lineTo_Callback";
+
+ @DomName('DOMPath.moveTo')
+ @DocsEditable
+ void moveTo(num x, num y) native "DOMPath_moveTo_Callback";
+
+ @DomName('DOMPath.quadraticCurveTo')
+ @DocsEditable
+ void quadraticCurveTo(num cpx, num cpy, num x, num y) native "DOMPath_quadraticCurveTo_Callback";
+
+ @DomName('DOMPath.rect')
+ @DocsEditable
+ void rect(num x, num y, num width, num height) native "DOMPath_rect_Callback";
+
+}
+// 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.
+
+// WARNING: Do not edit - generated code.
+
+
+@DocsEditable
@DomName('Performance')
@SupportedBrowser(SupportedBrowser.CHROME)
@SupportedBrowser(SupportedBrowser.FIREFOX)
@@ -18318,6 +17919,272 @@
@DocsEditable
+@DomName('Plugin')
+class Plugin extends NativeFieldWrapperClass1 {
+ Plugin.internal();
+
+ @DomName('DOMPlugin.description')
+ @DocsEditable
+ String get description native "DOMPlugin_description_Getter";
+
+ @DomName('DOMPlugin.filename')
+ @DocsEditable
+ String get filename native "DOMPlugin_filename_Getter";
+
+ @DomName('DOMPlugin.length')
+ @DocsEditable
+ int get length native "DOMPlugin_length_Getter";
+
+ @DomName('DOMPlugin.name')
+ @DocsEditable
+ String get name native "DOMPlugin_name_Getter";
+
+ @DomName('DOMPlugin.item')
+ @DocsEditable
+ MimeType item(int index) native "DOMPlugin_item_Callback";
+
+ @DomName('DOMPlugin.namedItem')
+ @DocsEditable
+ MimeType namedItem(String name) native "DOMPlugin_namedItem_Callback";
+
+}
+// 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.
+
+// WARNING: Do not edit - generated code.
+
+
+@DocsEditable
+@DomName('PluginArray')
+class PluginArray extends NativeFieldWrapperClass1 implements List<Plugin> {
+ PluginArray.internal();
+
+ @DomName('DOMPluginArray.length')
+ @DocsEditable
+ int get length native "DOMPluginArray_length_Getter";
+
+ Plugin operator[](int index) native "DOMPluginArray_item_Callback";
+
+ void operator[]=(int index, Plugin value) {
+ throw new UnsupportedError("Cannot assign element of immutable List.");
+ }
+ // -- start List<Plugin> mixins.
+ // Plugin is the element type.
+
+ // From Iterable<Plugin>:
+
+ Iterator<Plugin> get iterator {
+ // Note: NodeLists are not fixed size. And most probably length shouldn't
+ // be cached in both iterator _and_ forEach method. For now caching it
+ // for consistency.
+ return new FixedSizeListIterator<Plugin>(this);
+ }
+
+ Plugin reduce(Plugin combine(Plugin value, Plugin element)) {
+ return IterableMixinWorkaround.reduce(this, combine);
+ }
+
+ dynamic fold(dynamic initialValue,
+ dynamic combine(dynamic previousValue, Plugin element)) {
+ return IterableMixinWorkaround.fold(this, initialValue, combine);
+ }
+
+ bool contains(Plugin element) => IterableMixinWorkaround.contains(this, element);
+
+ void forEach(void f(Plugin element)) => IterableMixinWorkaround.forEach(this, f);
+
+ String join([String separator = ""]) =>
+ IterableMixinWorkaround.joinList(this, separator);
+
+ Iterable map(f(Plugin element)) =>
+ IterableMixinWorkaround.mapList(this, f);
+
+ Iterable<Plugin> where(bool f(Plugin element)) =>
+ IterableMixinWorkaround.where(this, f);
+
+ Iterable expand(Iterable f(Plugin element)) =>
+ IterableMixinWorkaround.expand(this, f);
+
+ bool every(bool f(Plugin element)) => IterableMixinWorkaround.every(this, f);
+
+ bool any(bool f(Plugin element)) => IterableMixinWorkaround.any(this, f);
+
+ List<Plugin> toList({ bool growable: true }) =>
+ new List<Plugin>.from(this, growable: growable);
+
+ Set<Plugin> toSet() => new Set<Plugin>.from(this);
+
+ bool get isEmpty => this.length == 0;
+
+ Iterable<Plugin> take(int n) => IterableMixinWorkaround.takeList(this, n);
+
+ Iterable<Plugin> takeWhile(bool test(Plugin value)) {
+ return IterableMixinWorkaround.takeWhile(this, test);
+ }
+
+ Iterable<Plugin> skip(int n) => IterableMixinWorkaround.skipList(this, n);
+
+ Iterable<Plugin> skipWhile(bool test(Plugin value)) {
+ return IterableMixinWorkaround.skipWhile(this, test);
+ }
+
+ Plugin firstWhere(bool test(Plugin value), { Plugin orElse() }) {
+ return IterableMixinWorkaround.firstWhere(this, test, orElse);
+ }
+
+ Plugin lastWhere(bool test(Plugin value), {Plugin orElse()}) {
+ return IterableMixinWorkaround.lastWhereList(this, test, orElse);
+ }
+
+ Plugin singleWhere(bool test(Plugin value)) {
+ return IterableMixinWorkaround.singleWhere(this, test);
+ }
+
+ Plugin elementAt(int index) {
+ return this[index];
+ }
+
+ // From Collection<Plugin>:
+
+ void add(Plugin value) {
+ throw new UnsupportedError("Cannot add to immutable List.");
+ }
+
+ void addAll(Iterable<Plugin> iterable) {
+ throw new UnsupportedError("Cannot add to immutable List.");
+ }
+
+ // From List<Plugin>:
+ void set length(int value) {
+ throw new UnsupportedError("Cannot resize immutable List.");
+ }
+
+ void clear() {
+ throw new UnsupportedError("Cannot clear immutable List.");
+ }
+
+ Iterable<Plugin> get reversed {
+ return IterableMixinWorkaround.reversedList(this);
+ }
+
+ void sort([int compare(Plugin a, Plugin b)]) {
+ throw new UnsupportedError("Cannot sort immutable List.");
+ }
+
+ int indexOf(Plugin element, [int start = 0]) =>
+ Lists.indexOf(this, element, start, this.length);
+
+ int lastIndexOf(Plugin element, [int start]) {
+ if (start == null) start = length - 1;
+ return Lists.lastIndexOf(this, element, start);
+ }
+
+ Plugin get first {
+ if (this.length > 0) return this[0];
+ throw new StateError("No elements");
+ }
+
+ Plugin get last {
+ if (this.length > 0) return this[this.length - 1];
+ throw new StateError("No elements");
+ }
+
+ Plugin get single {
+ if (length == 1) return this[0];
+ if (length == 0) throw new StateError("No elements");
+ throw new StateError("More than one element");
+ }
+
+ void insert(int index, Plugin element) {
+ throw new UnsupportedError("Cannot add to immutable List.");
+ }
+
+ void insertAll(int index, Iterable<Plugin> iterable) {
+ throw new UnsupportedError("Cannot add to immutable List.");
+ }
+
+ void setAll(int index, Iterable<Plugin> iterable) {
+ throw new UnsupportedError("Cannot modify an immutable List.");
+ }
+
+ Plugin removeAt(int pos) {
+ throw new UnsupportedError("Cannot remove from immutable List.");
+ }
+
+ Plugin removeLast() {
+ throw new UnsupportedError("Cannot remove from immutable List.");
+ }
+
+ bool remove(Object object) {
+ throw new UnsupportedError("Cannot remove from immutable List.");
+ }
+
+ void removeWhere(bool test(Plugin element)) {
+ throw new UnsupportedError("Cannot remove from immutable List.");
+ }
+
+ void retainWhere(bool test(Plugin element)) {
+ throw new UnsupportedError("Cannot remove from immutable List.");
+ }
+
+ void setRange(int start, int end, Iterable<Plugin> iterable, [int skipCount=0]) {
+ throw new UnsupportedError("Cannot setRange on immutable List.");
+ }
+
+ void removeRange(int start, int end) {
+ throw new UnsupportedError("Cannot removeRange on immutable List.");
+ }
+
+ void replaceRange(int start, int end, Iterable<Plugin> iterable) {
+ throw new UnsupportedError("Cannot modify an immutable List.");
+ }
+
+ void fillRange(int start, int end, [Plugin fillValue]) {
+ throw new UnsupportedError("Cannot modify an immutable List.");
+ }
+
+ Iterable<Plugin> getRange(int start, int end) =>
+ IterableMixinWorkaround.getRangeList(this, start, end);
+
+ List<Plugin> sublist(int start, [int end]) {
+ if (end == null) end = length;
+ return Lists.getRange(this, start, end, <Plugin>[]);
+ }
+
+ Map<int, Plugin> asMap() =>
+ IterableMixinWorkaround.asMapList(this);
+
+ String toString() {
+ StringBuffer buffer = new StringBuffer('[');
+ buffer.writeAll(this, ', ');
+ buffer.write(']');
+ return buffer.toString();
+ }
+
+ // -- end List<Plugin> mixins.
+
+ @DomName('DOMPluginArray.item')
+ @DocsEditable
+ Plugin item(int index) native "DOMPluginArray_item_Callback";
+
+ @DomName('DOMPluginArray.namedItem')
+ @DocsEditable
+ Plugin namedItem(String name) native "DOMPluginArray_namedItem_Callback";
+
+ @DomName('DOMPluginArray.refresh')
+ @DocsEditable
+ void refresh(bool reload) native "DOMPluginArray_refresh_Callback";
+
+}
+// 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.
+
+// WARNING: Do not edit - generated code.
+
+
+@DocsEditable
@DomName('PopStateEvent')
@SupportedBrowser(SupportedBrowser.CHROME)
@SupportedBrowser(SupportedBrowser.FIREFOX)
@@ -18699,6 +18566,15 @@
@DocsEditable
String toString() native "Range_toString_Callback";
+
+ /**
+ * Checks if createContextualFragment is supported.
+ *
+ * See also:
+ *
+ * * [createContextualFragment]
+ */
+ static bool get supportsCreateContextualFragment => true;
}
// 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
@@ -19547,6 +19423,79 @@
@DocsEditable
+@DomName('SecurityPolicy')
+class SecurityPolicy extends NativeFieldWrapperClass1 {
+ SecurityPolicy.internal();
+
+ @DomName('DOMSecurityPolicy.allowsEval')
+ @DocsEditable
+ bool get allowsEval native "DOMSecurityPolicy_allowsEval_Getter";
+
+ @DomName('DOMSecurityPolicy.allowsInlineScript')
+ @DocsEditable
+ bool get allowsInlineScript native "DOMSecurityPolicy_allowsInlineScript_Getter";
+
+ @DomName('DOMSecurityPolicy.allowsInlineStyle')
+ @DocsEditable
+ bool get allowsInlineStyle native "DOMSecurityPolicy_allowsInlineStyle_Getter";
+
+ @DomName('DOMSecurityPolicy.isActive')
+ @DocsEditable
+ bool get isActive native "DOMSecurityPolicy_isActive_Getter";
+
+ @DomName('DOMSecurityPolicy.reportURIs')
+ @DocsEditable
+ List<String> get reportURIs native "DOMSecurityPolicy_reportURIs_Getter";
+
+ @DomName('DOMSecurityPolicy.allowsConnectionTo')
+ @DocsEditable
+ bool allowsConnectionTo(String url) native "DOMSecurityPolicy_allowsConnectionTo_Callback";
+
+ @DomName('DOMSecurityPolicy.allowsFontFrom')
+ @DocsEditable
+ bool allowsFontFrom(String url) native "DOMSecurityPolicy_allowsFontFrom_Callback";
+
+ @DomName('DOMSecurityPolicy.allowsFormAction')
+ @DocsEditable
+ bool allowsFormAction(String url) native "DOMSecurityPolicy_allowsFormAction_Callback";
+
+ @DomName('DOMSecurityPolicy.allowsFrameFrom')
+ @DocsEditable
+ bool allowsFrameFrom(String url) native "DOMSecurityPolicy_allowsFrameFrom_Callback";
+
+ @DomName('DOMSecurityPolicy.allowsImageFrom')
+ @DocsEditable
+ bool allowsImageFrom(String url) native "DOMSecurityPolicy_allowsImageFrom_Callback";
+
+ @DomName('DOMSecurityPolicy.allowsMediaFrom')
+ @DocsEditable
+ bool allowsMediaFrom(String url) native "DOMSecurityPolicy_allowsMediaFrom_Callback";
+
+ @DomName('DOMSecurityPolicy.allowsObjectFrom')
+ @DocsEditable
+ bool allowsObjectFrom(String url) native "DOMSecurityPolicy_allowsObjectFrom_Callback";
+
+ @DomName('DOMSecurityPolicy.allowsPluginType')
+ @DocsEditable
+ bool allowsPluginType(String type) native "DOMSecurityPolicy_allowsPluginType_Callback";
+
+ @DomName('DOMSecurityPolicy.allowsScriptFrom')
+ @DocsEditable
+ bool allowsScriptFrom(String url) native "DOMSecurityPolicy_allowsScriptFrom_Callback";
+
+ @DomName('DOMSecurityPolicy.allowsStyleFrom')
+ @DocsEditable
+ bool allowsStyleFrom(String url) native "DOMSecurityPolicy_allowsStyleFrom_Callback";
+
+}
+// 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.
+
+// WARNING: Do not edit - generated code.
+
+
+@DocsEditable
@DomName('SecurityPolicyViolationEvent')
class SecurityPolicyViolationEvent extends Event {
SecurityPolicyViolationEvent.internal() : super.internal();
@@ -19735,6 +19684,123 @@
@DocsEditable
+@DomName('Selection')
+class Selection extends NativeFieldWrapperClass1 {
+ Selection.internal();
+
+ @DomName('DOMSelection.anchorNode')
+ @DocsEditable
+ Node get anchorNode native "DOMSelection_anchorNode_Getter";
+
+ @DomName('DOMSelection.anchorOffset')
+ @DocsEditable
+ int get anchorOffset native "DOMSelection_anchorOffset_Getter";
+
+ @DomName('DOMSelection.baseNode')
+ @DocsEditable
+ Node get baseNode native "DOMSelection_baseNode_Getter";
+
+ @DomName('DOMSelection.baseOffset')
+ @DocsEditable
+ int get baseOffset native "DOMSelection_baseOffset_Getter";
+
+ @DomName('DOMSelection.extentNode')
+ @DocsEditable
+ Node get extentNode native "DOMSelection_extentNode_Getter";
+
+ @DomName('DOMSelection.extentOffset')
+ @DocsEditable
+ int get extentOffset native "DOMSelection_extentOffset_Getter";
+
+ @DomName('DOMSelection.focusNode')
+ @DocsEditable
+ Node get focusNode native "DOMSelection_focusNode_Getter";
+
+ @DomName('DOMSelection.focusOffset')
+ @DocsEditable
+ int get focusOffset native "DOMSelection_focusOffset_Getter";
+
+ @DomName('DOMSelection.isCollapsed')
+ @DocsEditable
+ bool get isCollapsed native "DOMSelection_isCollapsed_Getter";
+
+ @DomName('DOMSelection.rangeCount')
+ @DocsEditable
+ int get rangeCount native "DOMSelection_rangeCount_Getter";
+
+ @DomName('DOMSelection.type')
+ @DocsEditable
+ String get type native "DOMSelection_type_Getter";
+
+ @DomName('DOMSelection.addRange')
+ @DocsEditable
+ void addRange(Range range) native "DOMSelection_addRange_Callback";
+
+ @DomName('DOMSelection.collapse')
+ @DocsEditable
+ void collapse(Node node, int index) native "DOMSelection_collapse_Callback";
+
+ @DomName('DOMSelection.collapseToEnd')
+ @DocsEditable
+ void collapseToEnd() native "DOMSelection_collapseToEnd_Callback";
+
+ @DomName('DOMSelection.collapseToStart')
+ @DocsEditable
+ void collapseToStart() native "DOMSelection_collapseToStart_Callback";
+
+ @DomName('DOMSelection.containsNode')
+ @DocsEditable
+ bool containsNode(Node node, bool allowPartial) native "DOMSelection_containsNode_Callback";
+
+ @DomName('DOMSelection.deleteFromDocument')
+ @DocsEditable
+ void deleteFromDocument() native "DOMSelection_deleteFromDocument_Callback";
+
+ @DomName('DOMSelection.empty')
+ @DocsEditable
+ void empty() native "DOMSelection_empty_Callback";
+
+ @DomName('DOMSelection.extend')
+ @DocsEditable
+ void extend(Node node, int offset) native "DOMSelection_extend_Callback";
+
+ @DomName('DOMSelection.getRangeAt')
+ @DocsEditable
+ Range getRangeAt(int index) native "DOMSelection_getRangeAt_Callback";
+
+ @DomName('DOMSelection.modify')
+ @DocsEditable
+ void modify(String alter, String direction, String granularity) native "DOMSelection_modify_Callback";
+
+ @DomName('DOMSelection.removeAllRanges')
+ @DocsEditable
+ void removeAllRanges() native "DOMSelection_removeAllRanges_Callback";
+
+ @DomName('DOMSelection.selectAllChildren')
+ @DocsEditable
+ void selectAllChildren(Node node) native "DOMSelection_selectAllChildren_Callback";
+
+ @DomName('DOMSelection.setBaseAndExtent')
+ @DocsEditable
+ void setBaseAndExtent(Node baseNode, int baseOffset, Node extentNode, int extentOffset) native "DOMSelection_setBaseAndExtent_Callback";
+
+ @DomName('DOMSelection.setPosition')
+ @DocsEditable
+ void setPosition(Node node, int offset) native "DOMSelection_setPosition_Callback";
+
+ @DomName('DOMSelection.toString')
+ @DocsEditable
+ String toString() native "DOMSelection_toString_Callback";
+
+}
+// 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.
+
+// WARNING: Do not edit - generated code.
+
+
+@DocsEditable
@DomName('HTMLShadowElement')
@SupportedBrowser(SupportedBrowser.CHROME, '26')
@Experimental
@@ -19820,7 +19886,7 @@
@DomName('ShadowRoot.getSelection')
@DocsEditable
- DomSelection getSelection() native "ShadowRoot_getSelection_Callback";
+ Selection getSelection() native "ShadowRoot_getSelection_Callback";
static bool get supported => _Utils.shadowRootSupported(window.document);
}
@@ -23341,40 +23407,6 @@
@DocsEditable
-@DomName('WebKitCSSFilterRule')
-class WebKitCssFilterRule extends CssRule {
- WebKitCssFilterRule.internal() : super.internal();
-
- @DomName('WebKitCSSFilterRule.style')
- @DocsEditable
- CssStyleDeclaration get style native "WebKitCSSFilterRule_style_Getter";
-
-}
-// 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.
-
-// WARNING: Do not edit - generated code.
-
-
-@DocsEditable
-@DomName('WebKitCSSRegionRule')
-class WebKitCssRegionRule extends CssRule {
- WebKitCssRegionRule.internal() : super.internal();
-
- @DomName('WebKitCSSRegionRule.cssRules')
- @DocsEditable
- List<CssRule> get cssRules native "WebKitCSSRegionRule_cssRules_Getter";
-
-}
-// 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.
-
-// WARNING: Do not edit - generated code.
-
-
-@DocsEditable
/**
* Use the WebSocket interface to connect to a WebSocket,
* and to send and receive data on that WebSocket.
@@ -24107,7 +24139,7 @@
@DomName('DOMWindow.getSelection')
@DocsEditable
- DomSelection getSelection() native "DOMWindow_getSelection_Callback";
+ Selection getSelection() native "DOMWindow_getSelection_Callback";
@DomName('DOMWindow.matchMedia')
@DocsEditable
@@ -25717,6 +25749,9 @@
@SupportedBrowser(SupportedBrowser.CHROME)
@SupportedBrowser(SupportedBrowser.SAFARI)
@Experimental
+@SupportedBrowser(SupportedBrowser.CHROME)
+@SupportedBrowser(SupportedBrowser.SAFARI)
+@Experimental
class _DomPoint extends NativeFieldWrapperClass1 {
_DomPoint.internal();
factory _DomPoint(num x, num y) => _create(x, y);
@@ -28199,78 +28234,28 @@
// BSD-style license that can be found in the LICENSE file.
+/** A Set that stores the CSS class names for an element. */
abstract class CssClassSet implements Set<String> {
- String toString() {
- return readClasses().join(' ');
- }
-
/**
* Adds the class [value] to the element if it is not on it, removes it if it
* is.
*/
- bool toggle(String value) {
- Set<String> s = readClasses();
- bool result = false;
- if (s.contains(value)) {
- s.remove(value);
- } else {
- s.add(value);
- result = true;
- }
- writeClasses(s);
- return result;
- }
+ bool toggle(String value);
/**
* Returns [:true:] if classes cannot be added or removed from this
* [:CssClassSet:].
*/
- bool get frozen => false;
+ bool get frozen;
- // interface Iterable - BEGIN
- Iterator<String> get iterator => readClasses().iterator;
- // interface Iterable - END
-
- // interface Collection - BEGIN
- void forEach(void f(String element)) {
- readClasses().forEach(f);
- }
-
- String join([String separator = ""]) => readClasses().join(separator);
-
- Iterable map(f(String element)) => readClasses().map(f);
-
- Iterable<String> where(bool f(String element)) => readClasses().where(f);
-
- Iterable expand(Iterable f(String element)) => readClasses().expand(f);
-
- bool every(bool f(String element)) => readClasses().every(f);
-
- bool any(bool f(String element)) => readClasses().any(f);
-
- bool get isEmpty => readClasses().isEmpty;
-
- int get length => readClasses().length;
-
- String reduce(String combine(String value, String element)) {
- return readClasses().reduce(combine);
- }
-
- dynamic fold(dynamic initialValue,
- dynamic combine(dynamic previousValue, String element)) {
- return readClasses().fold(initialValue, combine);
- }
- // interface Collection - END
-
- // interface Set - BEGIN
/**
* Determine if this element contains the class [value].
*
* This is the Dart equivalent of jQuery's
* [hasClass](http://api.jquery.com/hasClass/).
*/
- bool contains(String value) => readClasses().contains(value);
+ bool contains(String value);
/**
* Add the class [value] to element.
@@ -28278,11 +28263,7 @@
* This is the Dart equivalent of jQuery's
* [addClass](http://api.jquery.com/addClass/).
*/
- void add(String value) {
- // TODO - figure out if we need to do any validation here
- // or if the browser natively does enough.
- _modify((s) => s.add(value));
- }
+ void add(String value);
/**
* Remove the class [value] from element, and return true on successful
@@ -28291,13 +28272,7 @@
* This is the Dart equivalent of jQuery's
* [removeClass](http://api.jquery.com/removeClass/).
*/
- bool remove(Object value) {
- if (value is! String) return false;
- Set<String> s = readClasses();
- bool result = s.remove(value);
- writeClasses(s);
- return result;
- }
+ bool remove(Object value);
/**
* Add all classes specified in [iterable] to element.
@@ -28305,10 +28280,7 @@
* This is the Dart equivalent of jQuery's
* [addClass](http://api.jquery.com/addClass/).
*/
- void addAll(Iterable<String> iterable) {
- // TODO - see comment above about validation.
- _modify((s) => s.addAll(iterable));
- }
+ void addAll(Iterable<String> iterable);
/**
* Remove all classes specified in [iterable] from element.
@@ -28316,9 +28288,7 @@
* This is the Dart equivalent of jQuery's
* [removeClass](http://api.jquery.com/removeClass/).
*/
- void removeAll(Iterable<String> iterable) {
- _modify((s) => s.removeAll(iterable));
- }
+ void removeAll(Iterable<String> iterable);
/**
* Toggles all classes specified in [iterable] on element.
@@ -28327,59 +28297,35 @@
* remove it if it is. This is the Dart equivalent of jQuery's
* [toggleClass](http://api.jquery.com/toggleClass/).
*/
- void toggleAll(Iterable<String> iterable) {
- iterable.forEach(toggle);
+ void toggleAll(Iterable<String> iterable);
+}
+
+/**
+ * A set (union) of the CSS classes that are present in a set of elements.
+ * Implemented separately from _ElementCssClassSet for performance.
+ */
+class _MultiElementCssClassSet extends CssClassSetImpl {
+ final Iterable<Element> _elementIterable;
+ Iterable<_ElementCssClassSet> _elementCssClassSetIterable;
+
+ _MultiElementCssClassSet(this._elementIterable) {
+ _elementCssClassSetIterable = new List.from(_elementIterable).map(
+ (e) => new _ElementCssClassSet(e));
}
- void retainAll(Iterable<String> iterable) {
- _modify((s) => s.retainAll(iterable));
+ Set<String> readClasses() {
+ var s = new LinkedHashSet<String>();
+ _elementCssClassSetIterable.forEach((e) => s.addAll(e.readClasses()));
+ return s;
}
- void removeWhere(bool test(String name)) {
- _modify((s) => s.removeWhere(test));
+ void writeClasses(Set<String> s) {
+ var classes = new List.from(s).join(' ');
+ for (Element e in _elementIterable) {
+ e.$dom_className = classes;
+ }
}
- void retainWhere(bool test(String name)) {
- _modify((s) => s.retainWhere(test));
- }
-
- bool containsAll(Iterable<String> collection) =>
- readClasses().containsAll(collection);
-
- Set<String> intersection(Set<String> other) =>
- readClasses().intersection(other);
-
- Set<String> union(Set<String> other) =>
- readClasses().union(other);
-
- Set<String> difference(Set<String> other) =>
- readClasses().difference(other);
-
- String get first => readClasses().first;
- String get last => readClasses().last;
- String get single => readClasses().single;
- List<String> toList({ bool growable: true }) =>
- readClasses().toList(growable: growable);
- Set<String> toSet() => readClasses().toSet();
- Iterable<String> take(int n) => readClasses().take(n);
- Iterable<String> takeWhile(bool test(String value)) =>
- readClasses().takeWhile(test);
- Iterable<String> skip(int n) => readClasses().skip(n);
- Iterable<String> skipWhile(bool test(String value)) =>
- readClasses().skipWhile(test);
- String firstWhere(bool test(String value), { String orElse() }) =>
- readClasses().firstWhere(test, orElse: orElse);
- String lastWhere(bool test(String value), {String orElse()}) =>
- readClasses().lastWhere(test, orElse: orElse);
- String singleWhere(bool test(String value)) =>
- readClasses().singleWhere(test);
- String elementAt(int index) => readClasses().elementAt(index);
-
- void clear() {
- _modify((s) => s.clear());
- }
- // interface Set - END
-
/**
* Helper method used to modify the set of css classes on this element.
*
@@ -28389,25 +28335,53 @@
* After f returns, the modified set is written to the
* className property of this element.
*/
- void _modify( f(Set<String> s)) {
- Set<String> s = readClasses();
- f(s);
- writeClasses(s);
+ void modify( f(Set<String> s)) {
+ _elementCssClassSetIterable.forEach((e) => e.modify(f));
}
/**
- * Read the class names from the Element class property,
- * and put them into a set (duplicates are discarded).
- * This is intended to be overridden by specific implementations.
+ * Adds the class [value] to the element if it is not on it, removes it if it
+ * is.
*/
- Set<String> readClasses();
+ bool toggle(String value) =>
+ _modifyWithReturnValue((e) => e.toggle(value));
/**
- * Join all the elements of a set into one string and write
- * back to the element.
- * This is intended to be overridden by specific implementations.
+ * Remove the class [value] from element, and return true on successful
+ * removal.
+ *
+ * This is the Dart equivalent of jQuery's
+ * [removeClass](http://api.jquery.com/removeClass/).
*/
- void writeClasses(Set<String> s);
+ bool remove(Object value) => _modifyWithReturnValue((e) => e.remove(value));
+
+ bool _modifyWithReturnValue(f) => _elementCssClassSetIterable.fold(
+ false, (prevValue, element) => f(element) || prevValue);
+}
+
+class _ElementCssClassSet extends CssClassSetImpl {
+
+ final Element _element;
+
+ _ElementCssClassSet(this._element);
+
+ Set<String> readClasses() {
+ var s = new LinkedHashSet<String>();
+ var classname = _element.$dom_className;
+
+ for (String name in classname.split(' ')) {
+ String trimmed = name.trim();
+ if (!trimmed.isEmpty) {
+ s.add(trimmed);
+ }
+ }
+ return s;
+ }
+
+ void writeClasses(Set<String> s) {
+ List list = new List.from(s);
+ _element.$dom_className = s.join(' ');
+ }
}
// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
diff --git a/sdk/lib/html/html_common/css_class_set.dart b/sdk/lib/html/html_common/css_class_set.dart
new file mode 100644
index 0000000..df50d62
--- /dev/null
+++ b/sdk/lib/html/html_common/css_class_set.dart
@@ -0,0 +1,216 @@
+// 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 html_common;
+
+abstract class CssClassSetImpl implements CssClassSet {
+
+ String toString() {
+ return readClasses().join(' ');
+ }
+
+ /**
+ * Adds the class [value] to the element if it is not on it, removes it if it
+ * is.
+ */
+ bool toggle(String value) {
+ Set<String> s = readClasses();
+ bool result = false;
+ if (s.contains(value)) {
+ s.remove(value);
+ } else {
+ s.add(value);
+ result = true;
+ }
+ writeClasses(s);
+ return result;
+ }
+
+ /**
+ * Returns [:true:] if classes cannot be added or removed from this
+ * [:CssClassSet:].
+ */
+ bool get frozen => false;
+
+ // interface Iterable - BEGIN
+ Iterator<String> get iterator => readClasses().iterator;
+ // interface Iterable - END
+
+ // interface Collection - BEGIN
+ void forEach(void f(String element)) {
+ readClasses().forEach(f);
+ }
+
+ String join([String separator = ""]) => readClasses().join(separator);
+
+ Iterable map(f(String element)) => readClasses().map(f);
+
+ Iterable<String> where(bool f(String element)) => readClasses().where(f);
+
+ Iterable expand(Iterable f(String element)) => readClasses().expand(f);
+
+ bool every(bool f(String element)) => readClasses().every(f);
+
+ bool any(bool f(String element)) => readClasses().any(f);
+
+ bool get isEmpty => readClasses().isEmpty;
+
+ int get length => readClasses().length;
+
+ String reduce(String combine(String value, String element)) {
+ return readClasses().reduce(combine);
+ }
+
+ dynamic fold(dynamic initialValue,
+ dynamic combine(dynamic previousValue, String element)) {
+ return readClasses().fold(initialValue, combine);
+ }
+ // interface Collection - END
+
+ // interface Set - BEGIN
+ /**
+ * Determine if this element contains the class [value].
+ *
+ * This is the Dart equivalent of jQuery's
+ * [hasClass](http://api.jquery.com/hasClass/).
+ */
+ bool contains(String value) => readClasses().contains(value);
+
+ /**
+ * Add the class [value] to element.
+ *
+ * This is the Dart equivalent of jQuery's
+ * [addClass](http://api.jquery.com/addClass/).
+ */
+ void add(String value) {
+ // TODO - figure out if we need to do any validation here
+ // or if the browser natively does enough.
+ modify((s) => s.add(value));
+ }
+
+ /**
+ * Remove the class [value] from element, and return true on successful
+ * removal.
+ *
+ * This is the Dart equivalent of jQuery's
+ * [removeClass](http://api.jquery.com/removeClass/).
+ */
+ bool remove(Object value) {
+ if (value is! String) return false;
+ Set<String> s = readClasses();
+ bool result = s.remove(value);
+ writeClasses(s);
+ return result;
+ }
+
+ /**
+ * Add all classes specified in [iterable] to element.
+ *
+ * This is the Dart equivalent of jQuery's
+ * [addClass](http://api.jquery.com/addClass/).
+ */
+ void addAll(Iterable<String> iterable) {
+ // TODO - see comment above about validation.
+ modify((s) => s.addAll(iterable));
+ }
+
+ /**
+ * Remove all classes specified in [iterable] from element.
+ *
+ * This is the Dart equivalent of jQuery's
+ * [removeClass](http://api.jquery.com/removeClass/).
+ */
+ void removeAll(Iterable<String> iterable) {
+ modify((s) => s.removeAll(iterable));
+ }
+
+ /**
+ * Toggles all classes specified in [iterable] on element.
+ *
+ * Iterate through [iterable]'s items, and add it if it is not on it, or
+ * remove it if it is. This is the Dart equivalent of jQuery's
+ * [toggleClass](http://api.jquery.com/toggleClass/).
+ */
+ void toggleAll(Iterable<String> iterable) {
+ iterable.forEach(toggle);
+ }
+
+ void retainAll(Iterable<String> iterable) {
+ modify((s) => s.retainAll(iterable));
+ }
+
+ void removeWhere(bool test(String name)) {
+ modify((s) => s.removeWhere(test));
+ }
+
+ void retainWhere(bool test(String name)) {
+ modify((s) => s.retainWhere(test));
+ }
+
+ bool containsAll(Iterable<String> collection) =>
+ readClasses().containsAll(collection);
+
+ Set<String> intersection(Set<String> other) =>
+ readClasses().intersection(other);
+
+ Set<String> union(Set<String> other) =>
+ readClasses().union(other);
+
+ Set<String> difference(Set<String> other) =>
+ readClasses().difference(other);
+
+ String get first => readClasses().first;
+ String get last => readClasses().last;
+ String get single => readClasses().single;
+ List<String> toList({ bool growable: true }) =>
+ readClasses().toList(growable: growable);
+ Set<String> toSet() => readClasses().toSet();
+ Iterable<String> take(int n) => readClasses().take(n);
+ Iterable<String> takeWhile(bool test(String value)) =>
+ readClasses().takeWhile(test);
+ Iterable<String> skip(int n) => readClasses().skip(n);
+ Iterable<String> skipWhile(bool test(String value)) =>
+ readClasses().skipWhile(test);
+ String firstWhere(bool test(String value), { String orElse() }) =>
+ readClasses().firstWhere(test, orElse: orElse);
+ String lastWhere(bool test(String value), {String orElse()}) =>
+ readClasses().lastWhere(test, orElse: orElse);
+ String singleWhere(bool test(String value)) =>
+ readClasses().singleWhere(test);
+ String elementAt(int index) => readClasses().elementAt(index);
+
+ void clear() {
+ modify((s) => s.clear());
+ }
+ // interface Set - END
+
+ /**
+ * Helper method used to modify the set of css classes on this element.
+ *
+ * f - callback with:
+ * s - a Set of all the css class name currently on this element.
+ *
+ * After f returns, the modified set is written to the
+ * className property of this element.
+ */
+ void modify( f(Set<String> s)) {
+ Set<String> s = readClasses();
+ f(s);
+ writeClasses(s);
+ }
+
+ /**
+ * Read the class names from the Element class property,
+ * and put them into a set (duplicates are discarded).
+ * This is intended to be overridden by specific implementations.
+ */
+ Set<String> readClasses();
+
+ /**
+ * Join all the elements of a set into one string and write
+ * back to the element.
+ * This is intended to be overridden by specific implementations.
+ */
+ void writeClasses(Set<String> s);
+}
diff --git a/sdk/lib/html/html_common/html_common.dart b/sdk/lib/html/html_common/html_common.dart
index f023b30..72d5dab 100644
--- a/sdk/lib/html/html_common/html_common.dart
+++ b/sdk/lib/html/html_common/html_common.dart
@@ -1,4 +1,4 @@
-// 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.
@@ -10,6 +10,7 @@
import 'metadata.dart';
export 'metadata.dart';
+part 'css_class_set.dart';
part 'device.dart';
part 'filtered_element_list.dart';
part 'lists.dart';
diff --git a/sdk/lib/html/html_common/html_common_dart2js.dart b/sdk/lib/html/html_common/html_common_dart2js.dart
index 1f0a19f..ed42991 100644
--- a/sdk/lib/html/html_common/html_common_dart2js.dart
+++ b/sdk/lib/html/html_common/html_common_dart2js.dart
@@ -13,6 +13,7 @@
import 'metadata.dart';
export 'metadata.dart';
+part 'css_class_set.dart';
part 'conversions.dart';
part 'device.dart';
part 'filtered_element_list.dart';
diff --git a/sdk/lib/indexed_db/dart2js/indexed_db_dart2js.dart b/sdk/lib/indexed_db/dart2js/indexed_db_dart2js.dart
index a67b5d3..80671f8 100644
--- a/sdk/lib/indexed_db/dart2js/indexed_db_dart2js.dart
+++ b/sdk/lib/indexed_db/dart2js/indexed_db_dart2js.dart
@@ -464,7 +464,7 @@
* and errors out when the request errors.
*/
Future _completeRequest(Request request) {
- var completer = new Completer();
+ var completer = new Completer.sync();
// TODO: make sure that completer.complete is synchronous as transactions
// may be committed if the result is not processed immediately.
request.onSuccess.listen((e) {
diff --git a/sdk/lib/indexed_db/dartium/indexed_db_dartium.dart b/sdk/lib/indexed_db/dartium/indexed_db_dartium.dart
index 0d92e79..71b47ad 100644
--- a/sdk/lib/indexed_db/dartium/indexed_db_dartium.dart
+++ b/sdk/lib/indexed_db/dartium/indexed_db_dartium.dart
@@ -336,7 +336,7 @@
* and errors out when the request errors.
*/
Future _completeRequest(Request request) {
- var completer = new Completer();
+ var completer = new Completer.sync();
// TODO: make sure that completer.complete is synchronous as transactions
// may be committed if the result is not processed immediately.
request.onSuccess.listen((e) {
diff --git a/sdk/lib/io/file_impl.dart b/sdk/lib/io/file_impl.dart
index 89af6c3..b97b458 100644
--- a/sdk/lib/io/file_impl.dart
+++ b/sdk/lib/io/file_impl.dart
@@ -228,7 +228,8 @@
request[1] = _path;
return _fileService.call(request).then((response) {
if (_isErrorResponse(response)) {
- throw _exceptionFromResponse(response, "Cannot open file '$_path'");
+ throw _exceptionFromResponse(response,
+ "Cannot check existence of file '$_path'");
}
return response;
});
diff --git a/sdk/lib/io/http_body.dart b/sdk/lib/io/http_body.dart
index 7a8a494..1ec6d17 100644
--- a/sdk/lib/io/http_body.dart
+++ b/sdk/lib/io/http_body.dart
@@ -74,7 +74,7 @@
* The content type e.g. application/json, application/octet-stream,
* application/x-www-form-urlencoded, text/plain.
*/
- String get mimeType;
+ ContentType get contentType;
/**
* A high-level type value, that reflects how the body was parsed, e.g.
@@ -143,3 +143,26 @@
*/
HttpResponse get response;
}
+
+
+/**
+ * A [HttpBodyFileUpload] object wraps a file upload, presenting a way for
+ * extracting filename, contentType and the data of the uploaded file.
+ */
+abstract class HttpBodyFileUpload {
+ /**
+ * The filename of the uploaded file.
+ */
+ String filename;
+
+ /**
+ * The [ContentType] of the uploaded file. For 'text/\*' and
+ * 'application/json' the [data] field will a String.
+ */
+ ContentType contentType;
+
+ /**
+ * The content of the file. Either a [String] or a [List<int>].
+ */
+ dynamic content;
+}
diff --git a/sdk/lib/io/http_body_impl.dart b/sdk/lib/io/http_body_impl.dart
index 095b74e..736e86e 100644
--- a/sdk/lib/io/http_body_impl.dart
+++ b/sdk/lib/io/http_body_impl.dart
@@ -33,59 +33,134 @@
static Future<HttpBody> process(Stream<List<int>> stream,
HttpHeaders headers) {
- return stream.fold(
- new _BufferList(),
- (buffer, data) {
- // TODO(ajohnsen): Add limit for POST data.
- buffer.add(data);
- return buffer;
- })
- .then((list) {
- dynamic content = list.readBytes();
- String type = "binary";
- ContentType contentType = headers.contentType;
- if (contentType == null) {
- return new _HttpBody(null, type, content);
- }
- String asText(Encoding defaultEncoding) {
- var encoding;
- var charset = contentType.charset;
- if (charset != null) encoding = Encoding.fromName(charset);
- if (encoding == null) encoding = defaultEncoding;
- return _decodeString(content, encoding);
- }
- switch (contentType.primaryType) {
- case "text":
- type = "text";
- content = asText(Encoding.ASCII);
- break;
+ ContentType contentType = headers.contentType;
- case "application":
- switch (contentType.subType) {
- case "json":
- content = JSON.parse(asText(Encoding.UTF_8));
- type = "json";
- break;
+ Future<HttpBody> asBinary() {
+ return stream
+ .fold(new _BufferList(), (buffer, data) => buffer..add(data))
+ .then((buffer) => new _HttpBody(contentType,
+ "binary",
+ buffer.readBytes()));
+ }
- default:
- break;
+ Future<HttpBody> asText(Encoding defaultEncoding) {
+ var encoding;
+ var charset = contentType.charset;
+ if (charset != null) encoding = Encoding.fromName(charset);
+ if (encoding == null) encoding = defaultEncoding;
+ return stream
+ .transform(new StringDecoder(encoding))
+ .fold(new StringBuffer(), (buffer, data) => buffer..write(data))
+ .then((buffer) => new _HttpBody(contentType,
+ "text",
+ buffer.toString()));
+ }
+
+ Future<HttpBody> asFormData() {
+ return stream
+ .transform(new MimeMultipartTransformer(
+ contentType.parameters['boundary']))
+ .map((HttpMultipartFormData.parse))
+ .map((multipart) {
+ var future;
+ if (multipart.isText) {
+ future = multipart
+ .fold(new StringBuffer(), (b, s) => b..write(s))
+ .then((b) => b.toString());
+ } else {
+ future = multipart
+ .fold(new _BufferList(), (b, d) => b..add(d))
+ .then((b) => b.readBytes());
+ }
+ return future.then((data) {
+ var filename =
+ multipart.contentDisposition.parameters['filename'];
+ if (filename != null) {
+ data = new _HttpBodyFileUpload(multipart.contentType,
+ filename,
+ data);
}
- break;
+ return [multipart.contentDisposition.parameters['name'], data];
+ });
+ })
+ .fold([], (l, f) => l..add(f))
+ .then(Future.wait)
+ .then((parts) {
+ Map<String, dynamic> map = new Map<String, dynamic>();
+ for (var part in parts) {
+ map[part[0]] = part[1]; // Override existing entries.
+ }
+ return new _HttpBody(contentType, 'form', map);
+ });
+ }
- default:
- break;
- }
- return new _HttpBody(contentType.mimeType, type, content);
- });
+ if (contentType == null) {
+ return asBinary();
+ }
+
+ switch (contentType.primaryType) {
+ case "text":
+ return asText(Encoding.ASCII);
+
+ case "application":
+ switch (contentType.subType) {
+ case "json":
+ return asText(Encoding.UTF_8)
+ .then((body) => new _HttpBody(contentType,
+ "json",
+ JSON.parse(body.body)));
+
+ case "x-www-form-urlencoded":
+ return asText(Encoding.ASCII)
+ .then((body) {
+ var map = _HttpUtils.splitQueryString(body.body);
+ var result = {};
+ String parse(String s) {
+ return _HttpUtils.decodeHttpEntityString(
+ _HttpUtils.decodeUrlEncodedString(s));
+ }
+ for (var key in map.keys) {
+ result[parse(key)] = parse(map[key]);
+ }
+ return new _HttpBody(contentType, "form", result);
+ });
+
+ default:
+ break;
+ }
+ break;
+
+ case "multipart":
+ switch (contentType.subType) {
+ case "form-data":
+ return asFormData();
+
+ default:
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return asBinary();
}
}
+class _HttpBodyFileUpload implements HttpBodyFileUpload {
+ final ContentType contentType;
+ final String filename;
+ final dynamic content;
+ _HttpBodyFileUpload(this.contentType, this.filename, this.content);
+}
+
class _HttpBody implements HttpBody {
- final String mimeType;
+ final ContentType contentType;
final String type;
final dynamic body;
- _HttpBody(String this.mimeType,
+ _HttpBody(ContentType this.contentType,
String this.type,
dynamic this.body);
}
@@ -97,7 +172,7 @@
final HttpResponse response;
_HttpRequestBody(HttpRequest request, HttpBody body)
- : super(body.mimeType, body.type, body.body),
+ : super(body.contentType, body.type, body.body),
method = request.method,
uri = request.uri,
headers = request.headers,
@@ -109,7 +184,7 @@
final HttpClientResponse response;
_HttpClientResponseBody(HttpClientResponse response, HttpBody body)
- : super(body.mimeType, body.type, body.body),
+ : super(body.contentType, body.type, body.body),
this.response = response;
int get statusCode => response.statusCode;
diff --git a/sdk/lib/io/http_headers.dart b/sdk/lib/io/http_headers.dart
index f6f756b..5983717 100644
--- a/sdk/lib/io/http_headers.dart
+++ b/sdk/lib/io/http_headers.dart
@@ -525,7 +525,7 @@
s[index] == parameterSeparator) break;
index++;
}
- return s.substring(start, index).toLowerCase();
+ return s.substring(start, index);
}
void expect(String expected) {
diff --git a/sdk/lib/io/http_impl.dart b/sdk/lib/io/http_impl.dart
index 8ea7174..2f5e15a 100644
--- a/sdk/lib/io/http_impl.dart
+++ b/sdk/lib/io/http_impl.dart
@@ -381,7 +381,33 @@
var cr = _httpClient._findProxyCredentials(proxy);
if (cr != null) {
- return retryWithProxyCredentials(cr);
+ if (cr.scheme == _AuthenticationScheme.BASIC) {
+ return retryWithProxyCredentials(cr);
+ }
+
+ // Digest authentication only supports the MD5 algorithm.
+ if (cr.scheme == _AuthenticationScheme.DIGEST &&
+ (header.parameters["algorithm"] == null ||
+ header.parameters["algorithm"].toLowerCase() == "md5")) {
+ if (cr.nonce == null || cr.nonce == header.parameters["nonce"]) {
+ // If the nonce is not set then this is the first authenticate
+ // response for these credentials. Set up authentication state.
+ if (cr.nonce == null) {
+ cr.nonce = header.parameters["nonce"];
+ cr.algorithm = "MD5";
+ cr.qop = header.parameters["qop"];
+ cr.nonceCount = 0;
+ }
+ // Credentials where found, prepare for retrying the request.
+ return retryWithProxyCredentials(cr);
+ } else if (header.parameters["stale"] != null &&
+ header.parameters["stale"].toLowerCase() == "true") {
+ // If stale is true retry with new nonce.
+ cr.nonce = header.parameters["nonce"];
+ // Credentials where found, prepare for retrying the request.
+ return retryWithProxyCredentials(cr);
+ }
+ }
}
// Ask for more credentials if none found.
@@ -914,6 +940,40 @@
_responseCompleter.completeError(error);
}
+ // Generate the request URI based on the method and proxy.
+ String _requestUri() {
+ // Generate the request URI starting from the path component.
+ String uriStartingFromPath() {
+ String result = uri.path;
+ if (result.length == 0) result = "/";
+ if (uri.query != "") {
+ if (uri.fragment != "") {
+ result = "${result}?${uri.query}#${uri.fragment}";
+ } else {
+ result = "${result}?${uri.query}";
+ }
+ }
+ return result;
+ }
+
+ if (_proxy.isDirect) {
+ return uriStartingFromPath();
+ } else {
+ if (method == "CONNECT") {
+ // For the connect method the request URI is the host:port of
+ // the requested destination of the tunnel (see RFC 2817
+ // section 5.2)
+ return "${uri.domain}:${uri.port}";
+ } else {
+ if (_httpClientConnection._proxyTunnel) {
+ return uriStartingFromPath();
+ } else {
+ return uri.toString();
+ }
+ }
+ }
+ }
+
void _writeHeader() {
var buffer = new _BufferList();
@@ -921,42 +981,13 @@
writeCRLF() => buffer.add(const [_CharCode.CR, _CharCode.LF]);
- void writePath() {
- String path = uri.path;
- if (path.length == 0) path = "/";
- if (uri.query != "") {
- if (uri.fragment != "") {
- path = "${path}?${uri.query}#${uri.fragment}";
- } else {
- path = "${path}?${uri.query}";
- }
- }
- buffer.add(path.codeUnits);
- }
-
// Write the request method.
buffer.add(method.codeUnits);
writeSP();
// Write the request URI.
- if (_proxy.isDirect) {
- writePath();
- } else {
- if (method == "CONNECT") {
- // For the connect method the request URI is the host:port of
- // the requested destination of the tunnel (see RFC 2817
- // section 5.2)
- buffer.add(uri.domain.codeUnits);
- buffer.add(const [_CharCode.COLON]);
- buffer.add(uri.port.toString().codeUnits);
- } else {
- if (_httpClientConnection._proxyTunnel) {
- writePath();
- } else {
- buffer.add(uri.toString().codeUnits);
- }
- }
- }
+ buffer.add(_requestUri().codeUnits);
writeSP();
+ // Write HTTP/1.1.
buffer.add(_Const.HTTP11);
writeCRLF();
@@ -1120,7 +1151,8 @@
_HttpClientRequest send(Uri uri, int port, String method, _Proxy proxy) {
// Start with pausing the parser.
_subscription.pause();
- _Credentials cr; // Credentials used to authorize this request.
+ _ProxyCredentials proxyCreds; // Credentials used to authorize proxy.
+ _SiteCredentials creds; // Credentials used to authorize this request.
var outgoing = new _HttpOutgoing(_socket);
// Create new request object, wrapping the outgoing connection.
var request = new _HttpClientRequest(outgoing,
@@ -1139,9 +1171,9 @@
_encodeString("${proxy.username}:${proxy.password}"));
request.headers.set(HttpHeaders.PROXY_AUTHORIZATION, "Basic $auth");
} else if (!proxy.isDirect && _httpClient._proxyCredentials.length > 0) {
- var cr = _httpClient._findProxyCredentials(proxy);
- if (cr != null) {
- cr.authorize(request);
+ proxyCreds = _httpClient._findProxyCredentials(proxy);
+ if (proxyCreds != null) {
+ proxyCreds.authorize(request);
}
}
if (uri.userInfo != null && !uri.userInfo.isEmpty) {
@@ -1152,9 +1184,9 @@
request.headers.set(HttpHeaders.AUTHORIZATION, "Basic $auth");
} else {
// Look for credentials.
- cr = _httpClient._findCredentials(uri);
- if (cr != null) {
- cr.authorize(request);
+ creds = _httpClient._findCredentials(uri);
+ if (creds != null) {
+ creds.authorize(request);
}
}
// Start sending the request (lazy, delayed until the user provides
@@ -1179,16 +1211,31 @@
destroy();
}
});
- // For digest authentication check if the server
- // requests the client to start using a new nonce.
- if (cr != null && cr.scheme == _AuthenticationScheme.DIGEST) {
+ // For digest authentication if proxy check if the proxy
+ // requests the client to start using a new nonce for proxy
+ // authentication.
+ if (proxyCreds != null &&
+ proxyCreds.scheme == _AuthenticationScheme.DIGEST) {
+ var authInfo = incoming.headers["proxy-authentication-info"];
+ if (authInfo != null && authInfo.length == 1) {
+ var header =
+ _HeaderValue.parse(
+ authInfo[0], parameterSeparator: ',');
+ var nextnonce = header.parameters["nextnonce"];
+ if (nextnonce != null) proxyCreds.nonce = nextnonce;
+ }
+ }
+ // For digest authentication check if the server requests the
+ // client to start using a new nonce.
+ if (creds != null &&
+ creds.scheme == _AuthenticationScheme.DIGEST) {
var authInfo = incoming.headers["authentication-info"];
if (authInfo != null && authInfo.length == 1) {
var header =
_HeaderValue.parse(
authInfo[0], parameterSeparator: ',');
var nextnonce = header.parameters["nextnonce"];
- if (nextnonce != null) cr.nonce = nextnonce;
+ if (nextnonce != null) creds.nonce = nextnonce;
}
}
request._onIncoming(incoming);
@@ -1354,7 +1401,7 @@
}
void addCredentials(Uri url, String realm, HttpClientCredentials cr) {
- _credentials.add(new _Credentials(url, realm, cr));
+ _credentials.add(new _SiteCredentials(url, realm, cr));
}
set authenticateProxy(
@@ -1527,10 +1574,10 @@
return connect(new HttpException("No proxies given"));
}
- _Credentials _findCredentials(Uri url, [_AuthenticationScheme scheme]) {
+ _SiteCredentials _findCredentials(Uri url, [_AuthenticationScheme scheme]) {
// Look for credentials.
- _Credentials cr =
- _credentials.fold(null, (_Credentials prev, _Credentials value) {
+ _SiteCredentials cr =
+ _credentials.fold(null, (prev, value) {
if (value.applies(url, scheme)) {
if (prev == null) return value;
return value.uri.path.length > prev.uri.path.length ? value : prev;
@@ -2008,8 +2055,19 @@
}
-class _Credentials {
- _Credentials(this.uri, this.realm, this.credentials) {
+abstract class _Credentials {
+ _HttpClientCredentials credentials;
+ String realm;
+ bool used = false;
+
+ // Digest specific fields.
+ String ha1;
+ String nonce;
+ String algorithm;
+ String qop;
+ int nonceCount;
+
+ _Credentials(this.credentials, this.realm) {
if (credentials.scheme == _AuthenticationScheme.DIGEST) {
// Calculate the H(A1) value once. There is no mentioning of
// username/password encoding in RFC 2617. However there is an
@@ -2030,6 +2088,15 @@
_AuthenticationScheme get scheme => credentials.scheme;
+ void authorize(HttpClientRequest request);
+}
+
+class _SiteCredentials extends _Credentials {
+ Uri uri;
+
+ _SiteCredentials(this.uri, realm, _HttpClientCredentials creds)
+ : super(creds, realm);
+
bool applies(Uri uri, _AuthenticationScheme scheme) {
if (scheme != null && credentials.scheme != scheme) return false;
if (uri.domain != this.uri.domain) return false;
@@ -2050,38 +2117,32 @@
credentials.authorize(this, request);
used = true;
}
-
- bool used = false;
- Uri uri;
- String realm;
- _HttpClientCredentials credentials;
-
- // Digest specific fields.
- String ha1;
- String nonce;
- String algorithm;
- String qop;
- int nonceCount;
}
-class _ProxyCredentials {
- _ProxyCredentials(this.host, this.port, this.realm, this.credentials);
+class _ProxyCredentials extends _Credentials {
+ String host;
+ int port;
- _AuthenticationScheme get scheme => credentials.scheme;
+ _ProxyCredentials(this.host,
+ this.port,
+ realm,
+ _HttpClientCredentials creds)
+ : super(creds, realm);
bool applies(_Proxy proxy, _AuthenticationScheme scheme) {
return proxy.host == host && proxy.port == port;
}
void authorize(HttpClientRequest request) {
+ // Digest credentials cannot be used without a nonce from the
+ // server.
+ if (credentials.scheme == _AuthenticationScheme.DIGEST &&
+ nonce == null) {
+ return;
+ }
credentials.authorizeProxy(this, request);
}
-
- String host;
- int port;
- String realm;
- _HttpClientCredentials credentials;
}
@@ -2133,11 +2194,12 @@
_AuthenticationScheme get scheme => _AuthenticationScheme.DIGEST;
- String authorization(_Credentials credentials, HttpClientRequest request) {
+ String authorization(_Credentials credentials, _HttpClientRequest request) {
+ String requestUri = request._requestUri();
MD5 hasher = new MD5();
hasher.add(request.method.codeUnits);
hasher.add([_CharCode.COLON]);
- hasher.add(request.uri.path.codeUnits);
+ hasher.add(requestUri.codeUnits);
var ha2 = CryptoUtils.bytesToHex(hasher.close());
String qop;
@@ -2174,7 +2236,7 @@
buffer.write('username="$username"');
buffer.write(', realm="${credentials.realm}"');
buffer.write(', nonce="${credentials.nonce}"');
- buffer.write(', uri="${request.uri.path}"');
+ buffer.write(', uri="$requestUri"');
buffer.write(', algorithm="${credentials.algorithm}"');
if (qop == "auth") {
buffer.write(', qop="$qop"');
@@ -2186,13 +2248,14 @@
}
void authorize(_Credentials credentials, HttpClientRequest request) {
- request.headers.set(HttpHeaders.AUTHORIZATION, authorization(credentials, request));
+ request.headers.set(HttpHeaders.AUTHORIZATION,
+ authorization(credentials, request));
}
void authorizeProxy(_ProxyCredentials credentials,
HttpClientRequest request) {
- // TODO(sgjesse): Implement!!!
- throw new UnsupportedError("Digest authentication not yet supported");
+ request.headers.set(HttpHeaders.PROXY_AUTHORIZATION,
+ authorization(credentials, request));
}
String username;
diff --git a/sdk/lib/io/http_multipart_form_data.dart b/sdk/lib/io/http_multipart_form_data.dart
new file mode 100644
index 0000000..3c9bca4
--- /dev/null
+++ b/sdk/lib/io/http_multipart_form_data.dart
@@ -0,0 +1,73 @@
+// 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 dart.io;
+
+
+/**
+ * [:HttpMultipartFormData:] class used for 'upgrading' a [MimeMultipart] by
+ * parsing it as a 'multipart/form-data' part. The following code shows how
+ * it can be used.
+ *
+ * HttpServer server = ...;
+ * server.listen((request) {
+ * String boundary = request.headers.contentType.parameters['boundary'];
+ * request
+ * .transform(new MimeMultipartTransformer(boundary))
+ * .map(HttpMultipartFormData.parse)
+ * .map((HttpMultipartFormData formData) {
+ * // form data object available here.
+ * });
+ *
+ * [:HttpMultipartFormData:] is a Stream, serving either bytes or decoded
+ * Strings. Use [isText] or [isBinary] to see what type of data is provided.
+ */
+abstract class HttpMultipartFormData implements Stream {
+ /**
+ * The parsed [:Content-Type:] header of the [:HttpMultipartFormData:].
+ * Returns [:null:] if not present.
+ */
+ ContentType get contentType;
+
+ /**
+ * The parsed [:Content-Disposition:] header of the [:HttpMultipartFormData:].
+ * This field is always present. Use this to extract e.g. name(form field
+ * name)and filename (client provided name of uploaded file) parameters.
+ */
+ HeaderValue get contentDisposition;
+
+ /**
+ * The parsed [:Content-Transfer-Encoding:] header of the
+ * [:HttpMultipartFormData:]. This field is used to determine how to decode
+ * the data. Returns [:null:] if not present.
+ */
+ HeaderValue get contentTransferEncoding;
+
+ /**
+ * Returns [:true:] if the data is decoded as [String].
+ */
+ bool get isText;
+
+ /**
+ * Returns [:true:] if the data is raw bytes.
+ */
+ bool get isBinary;
+
+ /**
+ * Returns the value for the header named [name]. If there
+ * is no header with the provided name, [:null:] will be returned.
+ *
+ * Use this method to index other headers available in the original
+ * [MimeMultipart].
+ */
+ String value(String name);
+
+ /**
+ * Parse a [MimeMultipart] and return a [HttpMultipartFormData]. If the
+ * [:Content-Disposition:] header is missing or invalid, a [HttpException] is
+ * thrown.
+ */
+ static HttpMultipartFormData parse(MimeMultipart multipart)
+ => _HttpMultipartFormData.parse(multipart);
+}
diff --git a/sdk/lib/io/http_multipart_form_data_impl.dart b/sdk/lib/io/http_multipart_form_data_impl.dart
new file mode 100644
index 0000000..b227954
--- /dev/null
+++ b/sdk/lib/io/http_multipart_form_data_impl.dart
@@ -0,0 +1,102 @@
+// 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 dart.io;
+
+
+class _HttpMultipartFormData extends Stream implements HttpMultipartFormData {
+ final ContentType contentType;
+ final HeaderValue contentDisposition;
+ final HeaderValue contentTransferEncoding;
+
+ final MimeMultipart _mimeMultipart;
+
+ bool _isText = false;
+
+ Stream _stream;
+
+ _HttpMultipartFormData(ContentType this.contentType,
+ HeaderValue this.contentDisposition,
+ HeaderValue this.contentTransferEncoding,
+ MimeMultipart this._mimeMultipart) {
+ _stream = _mimeMultipart;
+ if (contentTransferEncoding != null) {
+ // TODO(ajohnsen): Support BASE64, etc.
+ throw new HttpException("Unsupported contentTransferEncoding: "
+ "${contentTransferEncoding.value}");
+ }
+
+ if (contentType == null ||
+ contentType.primaryType == 'text' ||
+ contentType.mimeType == 'application/json') {
+ _isText = true;
+ StringBuffer buffer = new StringBuffer();
+ Encoding encoding;
+ if (contentType != null) {
+ encoding = Encoding.fromName(contentType.charset);
+ }
+ if (encoding == null) encoding = Encoding.ISO_8859_1;
+ _stream = _stream
+ .transform(new StringDecoder(encoding))
+ .expand((data) {
+ buffer.write(data);
+ var out = _HttpUtils.decodeHttpEntityString(buffer.toString());
+ if (out != null) {
+ buffer = new StringBuffer();
+ return [out];
+ }
+ return const [];
+ });
+ }
+ }
+
+ bool get isText => _isText;
+ bool get isBinary => !_isText;
+
+ static HttpMultipartFormData parse(MimeMultipart multipart) {
+ var type;
+ var encoding;
+ var disposition;
+ var remaining = new Map<String, String>();
+ for (String key in multipart.headers.keys) {
+ switch (key) {
+ case HttpHeaders.CONTENT_TYPE:
+ type = ContentType.parse(multipart.headers[key]);
+ break;
+
+ case 'content-transfer-encoding':
+ encoding = HeaderValue.parse(multipart.headers[key]);
+ break;
+
+ case 'content-disposition':
+ disposition = HeaderValue.parse(multipart.headers[key]);
+ break;
+
+ default:
+ remaining[key] = multipart.headers[key];
+ break;
+ }
+ }
+ if (disposition == null) {
+ throw new HttpException(
+ "Mime Multipart doesn't contain a Content-Disposition header value");
+ }
+ return new _HttpMultipartFormData(type, disposition, encoding, multipart);
+ }
+
+ StreamSubscription listen(void onData(data),
+ {void onDone(),
+ void onError(error),
+ bool cancelOnError}) {
+ return _stream.listen(onData,
+ onDone: onDone,
+ onError: onError,
+ cancelOnError: cancelOnError);
+ }
+
+ String value(String name) {
+ return _mimeMultipart.headers[name];
+ }
+}
+
diff --git a/sdk/lib/io/http_utils.dart b/sdk/lib/io/http_utils.dart
index 8c23491..8e957bf 100644
--- a/sdk/lib/io/http_utils.dart
+++ b/sdk/lib/io/http_utils.dart
@@ -358,4 +358,41 @@
return new DateTime.utc(year, month, dayOfMonth, hour, minute, second, 0);
}
+
+ // Decode a string with HTTP entities. Returns null if the string ends in the
+ // middle of a http entity.
+ static String decodeHttpEntityString(String input) {
+ int amp = input.lastIndexOf('&');
+ if (amp < 0) return input;
+ int end = input.lastIndexOf(';');
+ if (end < amp) return null;
+
+ var buffer = new StringBuffer();
+ int offset = 0;
+
+ parse(amp, end) {
+ switch (input[amp + 1]) {
+ case '#':
+ if (input[amp + 2] == 'x') {
+ buffer.writeCharCode(
+ int.parse(input.substring(amp + 3, end), radix: 16));
+ } else {
+ buffer.writeCharCode(int.parse(input.substring(amp + 2, end)));
+ }
+ break;
+
+ default:
+ throw new HttpException('Unhandled HTTP entity token');
+ }
+ }
+
+ while ((amp = input.indexOf('&', offset)) >= 0) {
+ buffer.write(input.substring(offset, amp));
+ int end = input.indexOf(';', amp);
+ parse(amp, end);
+ offset = end + 1;
+ }
+ buffer.write(input.substring(offset));
+ return buffer.toString();
+ }
}
diff --git a/sdk/lib/io/io.dart b/sdk/lib/io/io.dart
index 2d7000f..fee6a12 100644
--- a/sdk/lib/io/io.dart
+++ b/sdk/lib/io/io.dart
@@ -40,6 +40,8 @@
part 'http_body_impl.dart';
part 'http_headers.dart';
part 'http_impl.dart';
+part 'http_multipart_form_data.dart';
+part 'http_multipart_form_data_impl.dart';
part 'http_parser.dart';
part 'http_session.dart';
part 'http_utils.dart';
diff --git a/sdk/lib/io/iolib_sources.gypi b/sdk/lib/io/iolib_sources.gypi
index f2d61f5..2eab1d9 100644
--- a/sdk/lib/io/iolib_sources.gypi
+++ b/sdk/lib/io/iolib_sources.gypi
@@ -19,6 +19,8 @@
'http_body_impl.dart',
'http_headers.dart',
'http_impl.dart',
+ 'http_multipart_form_data.dart',
+ 'http_multipart_form_data_impl.dart',
'http_parser.dart',
'http_session.dart',
'http_utils.dart',
diff --git a/sdk/lib/io/mime_multipart_parser.dart b/sdk/lib/io/mime_multipart_parser.dart
index 55451e9..4bfbebe 100644
--- a/sdk/lib/io/mime_multipart_parser.dart
+++ b/sdk/lib/io/mime_multipart_parser.dart
@@ -13,11 +13,11 @@
Map<String, String> get headers;
}
-class _MimeMultipartImpl extends MimeMultipart {
+class _MimeMultipart extends MimeMultipart {
final Map<String, String> headers;
final Stream<List<int>> _stream;
- _MimeMultipartImpl(this.headers, this._stream);
+ _MimeMultipart(this.headers, this._stream);
StreamSubscription<List<int>> listen(void onData(List<int> data),
{void onDone(),
@@ -64,7 +64,7 @@
List<int> _boundary;
int _state = _START;
- int _boundaryIndex = 0;
+ int _boundaryIndex = 2;
// Current index in the data buffer. If index is negative then it
// is the index into the artificial prefix of the boundary string.
@@ -119,7 +119,7 @@
onDone: () {
if (_state != _DONE) {
_controller.addError(
- new MimeParserException("Bad multipart ending"));
+ new MimeMultipartException("Bad multipart ending"));
}
_controller.close();
},
@@ -187,7 +187,7 @@
}
switch (_state) {
case _START:
- if (_toLowerCase(byte) == _toLowerCase(_boundary[_boundaryIndex])) {
+ if (byte == _boundary[_boundaryIndex]) {
_boundaryIndex++;
if (_boundaryIndex == _boundary.length) {
_state = _FIRST_BOUNDARY_ENDING;
@@ -246,7 +246,7 @@
_state = _HEADER_VALUE_START;
} else {
if (!_isTokenChar(byte)) {
- throw new MimeParserException("Invalid header field name");
+ throw new MimeMultipartException("Invalid header field name");
}
_headerField.writeCharCode(_toLowerCase(byte));
}
@@ -281,7 +281,7 @@
} else {
String headerField = _headerField.toString();
String headerValue =_headerValue.toString();
- _headers[headerField] = headerValue;
+ _headers[headerField.toLowerCase()] = headerValue;
_headerField = new StringBuffer();
_headerValue = new StringBuffer();
if (byte == _CharCode.CR) {
@@ -305,7 +305,7 @@
_parse();
});
_controller.add(
- new _MimeMultipartImpl(_headers, _multipartController.stream));
+ new _MimeMultipart(_headers, _multipartController.stream));
_headers = null;
_state = _CONTENT;
contentStartIndex = _index + 1;
@@ -388,20 +388,20 @@
void _expect(int val1, int val2) {
if (val1 != val2) {
- throw new MimeParserException("Failed to parse multipart mime 1");
+ throw new MimeMultipartException("Failed to parse multipart mime 1");
}
}
void _expectWS(int byte) {
if (byte != _CharCode.SP && byte != _CharCode.HT) {
- throw new MimeParserException("Failed to parse multipart mime 2");
+ throw new MimeMultipartException("Failed to parse multipart mime 2");
}
}
}
-class MimeParserException implements Exception {
- const MimeParserException([String this.message = ""]);
- String toString() => "MimeParserException: $message";
+class MimeMultipartException implements Exception {
+ const MimeMultipartException([String this.message = ""]);
+ String toString() => "MimeMultipartException: $message";
final String message;
}
diff --git a/sdk/lib/svg/dart2js/svg_dart2js.dart b/sdk/lib/svg/dart2js/svg_dart2js.dart
index c562e43..37ded8b 100644
--- a/sdk/lib/svg/dart2js/svg_dart2js.dart
+++ b/sdk/lib/svg/dart2js/svg_dart2js.dart
@@ -5744,7 +5744,7 @@
// BSD-style license that can be found in the LICENSE file.
-class _AttributeClassSet extends CssClassSet {
+class _AttributeClassSet extends CssClassSetImpl {
final Element _element;
_AttributeClassSet(this._element);
diff --git a/sdk/lib/svg/dartium/svg_dartium.dart b/sdk/lib/svg/dartium/svg_dartium.dart
index 3e42808..7cf7d6c 100644
--- a/sdk/lib/svg/dartium/svg_dartium.dart
+++ b/sdk/lib/svg/dartium/svg_dartium.dart
@@ -6485,7 +6485,7 @@
// BSD-style license that can be found in the LICENSE file.
-class _AttributeClassSet extends CssClassSet {
+class _AttributeClassSet extends CssClassSetImpl {
final Element _element;
_AttributeClassSet(this._element);
diff --git a/tests/co19/co19-analyzer.status b/tests/co19/co19-analyzer.status
new file mode 100644
index 0000000..4941e73
--- /dev/null
+++ b/tests/co19/co19-analyzer.status
@@ -0,0 +1,333 @@
+# Copyright (c) 2013, 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.
+
+[ $compiler == dartanalyzer ]
+Language/03_Overview/1_Scoping_A01_t46: fail
+Language/03_Overview/1_Scoping_A02_t07: fail
+Language/03_Overview/1_Scoping_A02_t11: fail
+Language/03_Overview/1_Scoping_A02_t12: fail
+Language/03_Overview/1_Scoping_A02_t16: fail
+Language/03_Overview/1_Scoping_A02_t28: fail
+Language/05_Variables/05_Variables_A01_t09: fail
+Language/05_Variables/05_Variables_A01_t11: fail
+Language/05_Variables/05_Variables_A05_t04: fail
+Language/06_Functions/06_Functions_A01_t10: fail
+Language/06_Functions/06_Functions_A01_t31: fail
+Language/06_Functions/2_Formal_Parameters/1_Required_Formals_A02_t06: fail
+Language/06_Functions/2_Formal_Parameters/1_Required_Formals_A02_t07: fail
+Language/06_Functions/2_Formal_Parameters/2_Optional_Formals_A03_t03: fail
+Language/06_Functions/2_Formal_Parameters_A01_t02: fail
+Language/06_Functions/2_Formal_Parameters_A01_t09: fail
+Language/06_Functions/2_Formal_Parameters_A02_t02: fail
+Language/07_Classes/07_Classes_A02_t02: fail
+Language/07_Classes/07_Classes_A02_t04: fail
+Language/07_Classes/07_Classes_A03_t02: fail
+Language/07_Classes/07_Classes_A03_t07: fail
+Language/07_Classes/07_Classes_A03_t08: fail
+Language/07_Classes/07_Classes_A09_t01: fail
+Language/07_Classes/07_Classes_A09_t02: fail
+Language/07_Classes/07_Classes_A09_t03: fail
+Language/07_Classes/07_Classes_A09_t04: fail
+Language/07_Classes/1_Instance_Methods/2_Operators_A03_t01: fail
+Language/07_Classes/1_Instance_Methods/2_Operators_A03_t02: fail
+Language/07_Classes/1_Instance_Methods/2_Operators_A04_t02: fail
+Language/07_Classes/1_Instance_Methods/2_Operators_A04_t03: fail
+Language/07_Classes/1_Instance_Methods/2_Operators_A04_t04: fail
+Language/07_Classes/1_Instance_Methods/2_Operators_A04_t05: fail
+Language/07_Classes/1_Instance_Methods/2_Operators_A04_t06: fail
+Language/07_Classes/1_Instance_Methods/2_Operators_A04_t07: fail
+Language/07_Classes/1_Instance_Methods/2_Operators_A04_t08: fail
+Language/07_Classes/1_Instance_Methods/2_Operators_A04_t09: fail
+Language/07_Classes/1_Instance_Methods/2_Operators_A04_t10: fail
+Language/07_Classes/1_Instance_Methods/2_Operators_A04_t11: fail
+Language/07_Classes/1_Instance_Methods/2_Operators_A04_t12: fail
+Language/07_Classes/1_Instance_Methods/2_Operators_A04_t13: fail
+Language/07_Classes/1_Instance_Methods/2_Operators_A04_t14: fail
+Language/07_Classes/1_Instance_Methods/2_Operators_A04_t15: fail
+Language/07_Classes/1_Instance_Methods/2_Operators_A04_t16: fail
+Language/07_Classes/1_Instance_Methods/2_Operators_A04_t17: fail
+Language/07_Classes/1_Instance_Methods/2_Operators_A04_t18: fail
+Language/07_Classes/1_Instance_Methods/2_Operators_A05_t02: fail
+Language/07_Classes/1_Instance_Methods/2_Operators_A06_t02: fail
+Language/07_Classes/1_Instance_Methods_A02_t05: fail
+Language/07_Classes/2_Getters_A04_t03: fail
+Language/07_Classes/2_Getters_A04_t04: fail
+Language/07_Classes/2_Getters_A04_t05: fail
+Language/07_Classes/2_Getters_A04_t07: fail
+Language/07_Classes/3_Setters_A03_t02: fail
+Language/07_Classes/3_Setters_A03_t04: fail
+Language/07_Classes/3_Setters_A03_t06: fail
+Language/07_Classes/3_Setters_A03_t08: fail
+Language/07_Classes/4_Abstract_Instance_Members_A04_t06: fail
+Language/07_Classes/6_Constructors/1_Generative_Constructors_A01_t02: fail
+Language/07_Classes/6_Constructors/1_Generative_Constructors_A01_t04: fail
+Language/07_Classes/6_Constructors/1_Generative_Constructors_A03_t03: fail
+Language/07_Classes/6_Constructors/1_Generative_Constructors_A03_t04: fail
+Language/07_Classes/6_Constructors/1_Generative_Constructors_A03_t05: fail
+Language/07_Classes/6_Constructors/1_Generative_Constructors_A03_t06: fail
+Language/07_Classes/6_Constructors/1_Generative_Constructors_A03_t07: fail
+Language/07_Classes/6_Constructors/1_Generative_Constructors_A03_t08: fail
+Language/07_Classes/6_Constructors/1_Generative_Constructors_A03_t11: fail
+Language/07_Classes/6_Constructors/1_Generative_Constructors_A04_t13: fail
+Language/07_Classes/6_Constructors/1_Generative_Constructors_A04_t15: fail
+Language/07_Classes/6_Constructors/1_Generative_Constructors_A05_t01: fail
+Language/07_Classes/6_Constructors/1_Generative_Constructors_A05_t02: fail
+Language/07_Classes/6_Constructors/1_Generative_Constructors_A05_t03: fail
+Language/07_Classes/6_Constructors/1_Generative_Constructors_A09_t01: fail
+Language/07_Classes/6_Constructors/1_Generative_Constructors_A11_t04: fail
+Language/07_Classes/6_Constructors/1_Generative_Constructors_A11_t05: fail
+Language/07_Classes/6_Constructors/1_Generative_Constructors_A11_t06: fail
+Language/07_Classes/6_Constructors/1_Generative_Constructors_A11_t07: fail
+Language/07_Classes/6_Constructors/1_Generative_Constructors_A11_t09: fail
+Language/07_Classes/6_Constructors/1_Generative_Constructors_A11_t12: fail
+Language/07_Classes/6_Constructors/1_Generative_Constructors_A14_t01: fail
+Language/07_Classes/6_Constructors/1_Generative_Constructors_A14_t02: fail
+Language/07_Classes/6_Constructors/1_Generative_Constructors_A14_t03: fail
+Language/07_Classes/6_Constructors/1_Generative_Constructors_A14_t04: fail
+Language/07_Classes/6_Constructors/1_Generative_Constructors_A14_t05: fail
+Language/07_Classes/6_Constructors/1_Generative_Constructors_A14_t06: fail
+Language/07_Classes/6_Constructors/1_Generative_Constructors_A15_t07: fail
+Language/07_Classes/6_Constructors/1_Generative_Constructors_A16_t01: fail
+Language/07_Classes/6_Constructors/1_Generative_Constructors_A16_t02: fail
+Language/07_Classes/6_Constructors/1_Generative_Constructors_A16_t07: fail
+Language/07_Classes/6_Constructors/2_Factories_A01_t05: fail
+Language/07_Classes/6_Constructors/2_Factories_A02_t01: fail
+Language/07_Classes/6_Constructors/2_Factories_A02_t02: fail
+Language/07_Classes/6_Constructors/2_Factories_A02_t03: fail
+Language/07_Classes/6_Constructors/2_Factories_A02_t04: fail
+Language/07_Classes/6_Constructors/2_Factories_A02_t05: fail
+Language/07_Classes/6_Constructors/3_Constant_Constructors_A03_t02: fail
+Language/07_Classes/6_Constructors/3_Constant_Constructors_A04_t01: fail
+Language/07_Classes/6_Constructors/3_Constant_Constructors_A04_t02: fail
+Language/07_Classes/6_Constructors/3_Constant_Constructors_A04_t03: fail
+Language/07_Classes/6_Constructors/3_Constant_Constructors_A05_t01: fail
+Language/07_Classes/6_Constructors/3_Constant_Constructors_A05_t02: fail
+Language/07_Classes/6_Constructors/3_Constant_Constructors_A05_t03: fail
+Language/07_Classes/6_Constructors_A02_t01: fail
+Language/07_Classes/7_Static_Methods_A01_t01: fail
+Language/07_Classes/9_Superclasses_A03_t01: fail
+Language/07_Classes/9_Superclasses_A03_t02: fail
+Language/08_Interfaces/5_Superinterfaces_A01_t03: fail
+Language/08_Interfaces/5_Superinterfaces_A01_t04: fail
+Language/08_Interfaces/5_Superinterfaces_A04_t03: fail
+Language/11_Expressions/01_Constants_A01_t01: fail
+Language/11_Expressions/01_Constants_A08_t02: fail
+Language/11_Expressions/01_Constants_A09_t02: fail
+Language/11_Expressions/01_Constants_A10_t03: fail
+Language/11_Expressions/01_Constants_A11_t03: fail
+Language/11_Expressions/01_Constants_A11_t04: fail
+Language/11_Expressions/01_Constants_A15_t07: fail
+Language/11_Expressions/01_Constants_A16_t01: fail
+Language/11_Expressions/01_Constants_A16_t02: fail
+Language/11_Expressions/01_Constants_A16_t03: fail
+Language/11_Expressions/01_Constants_A17_t03: fail
+Language/11_Expressions/01_Constants_A19_t04: fail
+Language/11_Expressions/01_Constants_A20_t02: fail
+Language/11_Expressions/03_Numbers_A01_t01: fail
+Language/11_Expressions/03_Numbers_A01_t02: fail
+Language/11_Expressions/03_Numbers_A01_t03: fail
+Language/11_Expressions/03_Numbers_A01_t04: fail
+Language/11_Expressions/03_Numbers_A01_t08: fail
+Language/11_Expressions/03_Numbers_A01_t10: fail
+Language/11_Expressions/05_Strings/1_String_Interpolation_A01_t03: fail
+Language/11_Expressions/05_Strings/1_String_Interpolation_A01_t09: fail
+Language/11_Expressions/05_Strings/1_String_Interpolation_A01_t11: fail
+Language/11_Expressions/05_Strings/1_String_Interpolation_A01_t14: fail
+Language/11_Expressions/05_Strings/1_String_Interpolation_A01_t15: fail
+Language/11_Expressions/05_Strings_A02_t34: fail
+Language/11_Expressions/05_Strings_A02_t36: fail
+Language/11_Expressions/05_Strings_A02_t42: fail
+Language/11_Expressions/05_Strings_A02_t44: fail
+Language/11_Expressions/05_Strings_A02_t46: fail
+Language/11_Expressions/05_Strings_A02_t48: fail
+Language/11_Expressions/06_Lists_A01_t03: fail
+Language/11_Expressions/06_Lists_A01_t04: fail
+Language/11_Expressions/06_Lists_A03_t01: fail
+Language/11_Expressions/07_Maps_A01_t01: fail
+Language/11_Expressions/07_Maps_A02_t02: fail
+Language/11_Expressions/07_Maps_A07_t03: fail
+Language/11_Expressions/08_Throw_A06_t01: fail
+Language/11_Expressions/08_Throw_A06_t02: fail
+Language/11_Expressions/08_Throw_A06_t03: fail
+Language/11_Expressions/08_Throw_A06_t04: fail
+Language/11_Expressions/08_Throw_A06_t05: fail
+Language/11_Expressions/08_Throw_A06_t06: fail
+Language/11_Expressions/10_This_A01_t05: fail
+Language/11_Expressions/10_This_A03_t01: fail
+Language/11_Expressions/10_This_A03_t03: fail
+Language/11_Expressions/10_This_A03_t04: fail
+Language/11_Expressions/10_This_A03_t05: fail
+Language/11_Expressions/10_This_A03_t06: fail
+Language/11_Expressions/10_This_A03_t07: fail
+Language/11_Expressions/10_This_A03_t08: fail
+Language/11_Expressions/11_Instance_Creation/1_New_A01_t04: fail
+Language/11_Expressions/11_Instance_Creation/2_Const_A02_t01: fail
+Language/11_Expressions/11_Instance_Creation/2_Const_A02_t02: fail
+Language/11_Expressions/11_Instance_Creation/2_Const_A02_t03: fail
+Language/11_Expressions/11_Instance_Creation/2_Const_A02_t04: fail
+Language/11_Expressions/11_Instance_Creation/2_Const_A02_t05: fail
+Language/11_Expressions/11_Instance_Creation/2_Const_A02_t06: fail
+Language/11_Expressions/11_Instance_Creation/2_Const_A02_t07: fail
+Language/11_Expressions/11_Instance_Creation/2_Const_A03_t01: fail
+Language/11_Expressions/11_Instance_Creation/2_Const_A03_t02: fail
+Language/11_Expressions/11_Instance_Creation/2_Const_A04_t01: fail
+Language/11_Expressions/11_Instance_Creation/2_Const_A04_t03: fail
+Language/11_Expressions/11_Instance_Creation/2_Const_A04_t04: fail
+Language/11_Expressions/11_Instance_Creation/2_Const_A05_t01: fail
+Language/11_Expressions/11_Instance_Creation/2_Const_A06_t01: fail
+Language/11_Expressions/11_Instance_Creation/2_Const_A06_t02: fail
+Language/11_Expressions/11_Instance_Creation/2_Const_A10_t01: fail
+Language/11_Expressions/11_Instance_Creation_A05_t02: fail
+Language/11_Expressions/14_Function_Invocation/1_Actual_Argument_List_Evaluation_A01_t05: fail
+Language/11_Expressions/14_Function_Invocation/2_Binding_Actuals_to_Formals_A04_t01: fail
+Language/11_Expressions/14_Function_Invocation/3_Unqualified_Invocation_A01_t10: fail
+Language/11_Expressions/15_Method_Invocation/1_Ordinary_Invocation_A01_t05: fail
+Language/11_Expressions/15_Method_Invocation/3_Static_Invocation_A01_t05: fail
+Language/11_Expressions/15_Method_Invocation/4_Super_Invocation_A01_t05: fail
+Language/11_Expressions/15_Method_Invocation/4_Super_Invocation_A05_t01: fail
+Language/11_Expressions/15_Method_Invocation/4_Super_Invocation_A05_t02: fail
+Language/11_Expressions/15_Method_Invocation/4_Super_Invocation_A05_t03: fail
+Language/11_Expressions/15_Method_Invocation/4_Super_Invocation_A05_t04: fail
+Language/11_Expressions/15_Method_Invocation/4_Super_Invocation_A05_t05: fail
+Language/11_Expressions/15_Method_Invocation/4_Super_Invocation_A05_t06: fail
+Language/11_Expressions/15_Method_Invocation/4_Super_Invocation_A05_t07: fail
+Language/11_Expressions/19_Conditional_A01_t05: fail
+Language/11_Expressions/19_Conditional_A01_t06: fail
+Language/11_Expressions/20_Logical_Boolean_Expressions_A01_t04: fail
+Language/11_Expressions/20_Logical_Boolean_Expressions_A01_t05: fail
+Language/11_Expressions/20_Logical_Boolean_Expressions_A01_t06: fail
+Language/11_Expressions/20_Logical_Boolean_Expressions_A01_t07: fail
+Language/11_Expressions/20_Logical_Boolean_Expressions_A01_t08: fail
+Language/11_Expressions/20_Logical_Boolean_Expressions_A01_t09: fail
+Language/11_Expressions/21_Bitwise_Expressions_A01_t03: fail
+Language/11_Expressions/21_Bitwise_Expressions_A01_t04: fail
+Language/11_Expressions/21_Bitwise_Expressions_A01_t05: fail
+Language/11_Expressions/21_Bitwise_Expressions_A01_t06: fail
+Language/11_Expressions/21_Bitwise_Expressions_A01_t07: fail
+Language/11_Expressions/21_Bitwise_Expressions_A01_t08: fail
+Language/11_Expressions/21_Bitwise_Expressions_A01_t09: fail
+Language/11_Expressions/21_Bitwise_Expressions_A01_t10: fail
+Language/11_Expressions/21_Bitwise_Expressions_A01_t11: fail
+Language/11_Expressions/22_Equality_A01_t03: fail
+Language/11_Expressions/22_Equality_A01_t04: fail
+Language/11_Expressions/22_Equality_A01_t07: fail
+Language/11_Expressions/22_Equality_A01_t08: fail
+Language/11_Expressions/22_Equality_A01_t11: fail
+Language/11_Expressions/22_Equality_A01_t12: fail
+Language/11_Expressions/22_Equality_A01_t15: fail
+Language/11_Expressions/22_Equality_A01_t16: fail
+Language/11_Expressions/23_Relational_Expressions_A01_t03: fail
+Language/11_Expressions/23_Relational_Expressions_A01_t04: fail
+Language/11_Expressions/23_Relational_Expressions_A01_t05: fail
+Language/11_Expressions/23_Relational_Expressions_A01_t06: fail
+Language/11_Expressions/23_Relational_Expressions_A01_t07: fail
+Language/11_Expressions/23_Relational_Expressions_A01_t08: fail
+Language/11_Expressions/23_Relational_Expressions_A01_t09: fail
+Language/11_Expressions/24_Shift_A01_t02: fail
+Language/11_Expressions/24_Shift_A01_t03: fail
+Language/11_Expressions/24_Shift_A01_t04: fail
+Language/11_Expressions/24_Shift_A01_t05: fail
+Language/11_Expressions/24_Shift_A01_t06: fail
+Language/11_Expressions/24_Shift_A01_t07: fail
+Language/11_Expressions/25_Additive_Expressions_A01_t02: fail
+Language/11_Expressions/25_Additive_Expressions_A01_t03: fail
+Language/11_Expressions/25_Additive_Expressions_A01_t05: fail
+Language/11_Expressions/25_Additive_Expressions_A01_t06: fail
+Language/11_Expressions/26_Multiplicative_Expressions_A01_t02: fail
+Language/11_Expressions/26_Multiplicative_Expressions_A01_t03: fail
+Language/11_Expressions/26_Multiplicative_Expressions_A01_t04: fail
+Language/11_Expressions/26_Multiplicative_Expressions_A01_t05: fail
+Language/11_Expressions/26_Multiplicative_Expressions_A01_t06: fail
+Language/11_Expressions/26_Multiplicative_Expressions_A01_t07: fail
+Language/11_Expressions/26_Multiplicative_Expressions_A01_t08: fail
+Language/11_Expressions/26_Multiplicative_Expressions_A01_t18: fail
+Language/11_Expressions/26_Multiplicative_Expressions_A01_t19: fail
+Language/11_Expressions/26_Multiplicative_Expressions_A01_t20: fail
+Language/11_Expressions/26_Multiplicative_Expressions_A01_t21: fail
+Language/11_Expressions/26_Multiplicative_Expressions_A01_t22: fail
+Language/11_Expressions/26_Multiplicative_Expressions_A01_t23: fail
+Language/11_Expressions/26_Multiplicative_Expressions_A01_t24: fail
+Language/11_Expressions/26_Multiplicative_Expressions_A01_t25: fail
+Language/11_Expressions/27_Unary_Expressions_A01_t08: fail
+Language/11_Expressions/28_Postfix_Expressions_A01_t06: fail
+Language/11_Expressions/28_Postfix_Expressions_A01_t09: fail
+Language/11_Expressions/28_Postfix_Expressions_A01_t10: fail
+Language/11_Expressions/28_Postfix_Expressions_A01_t11: fail
+Language/11_Expressions/29_Assignable_Expressions_A01_t10: fail
+Language/11_Expressions/29_Assignable_Expressions_A01_t11: fail
+Language/11_Expressions/29_Assignable_Expressions_A01_t12: fail
+Language/11_Expressions/29_Assignable_Expressions_A01_t13: fail
+Language/11_Expressions/29_Assignable_Expressions_A01_t15: fail
+Language/11_Expressions/29_Assignable_Expressions_A01_t16: fail
+Language/11_Expressions/30_Identifier_Reference_A01_t07: fail
+Language/11_Expressions/30_Identifier_Reference_A01_t08: fail
+Language/11_Expressions/31_Type_Test_A05_t01: fail
+Language/11_Expressions/31_Type_Test_A05_t02: fail
+Language/11_Expressions/31_Type_Test_A05_t03: fail
+Language/11_Expressions/32_Type_Cast_A04_t01: fail
+Language/11_Expressions/32_Type_Cast_A04_t02: fail
+Language/11_Expressions/33_Argument_Definition_Test_A01_t14: fail
+Language/11_Expressions/33_Argument_Definition_Test_A01_t18: fail
+Language/12_Statements/02_Expression_Statements_A01_t06: fail
+Language/12_Statements/02_Expression_Statements_A01_t07: fail
+Language/12_Statements/02_Expression_Statements_A01_t12: fail
+Language/12_Statements/03_Variable_Declaration_A04_t01: fail
+Language/12_Statements/03_Variable_Declaration_A04_t02: fail
+Language/12_Statements/03_Variable_Declaration_A04_t05: fail
+Language/12_Statements/03_Variable_Declaration_A04_t06: fail
+Language/12_Statements/03_Variable_Declaration_A04_t07: fail
+Language/12_Statements/03_Variable_Declaration_A04_t08: fail
+Language/12_Statements/04_Local_Function_Declaration_A01_t01: fail
+Language/12_Statements/04_Local_Function_Declaration_A02_t02: fail
+Language/12_Statements/05_If_A01_t01: fail
+Language/12_Statements/06_For_A01_t10: fail
+Language/12_Statements/07_While_A01_t04: fail
+Language/12_Statements/08_Do_A01_t07: fail
+Language/12_Statements/09_Switch_A01_t06: fail
+Language/12_Statements/15_Assert_A01_t03: fail
+Language/13_Libraries_and_Scripts/13_Libraries_and_Scripts_A03_t17: fail
+Language/13_Libraries_and_Scripts/1_Imports_A01_t46: fail
+Language/13_Libraries_and_Scripts/1_Imports_A02_t12: fail
+Language/13_Libraries_and_Scripts/1_Imports_A02_t15: fail
+Language/13_Libraries_and_Scripts/1_Imports_A03_t02: fail
+Language/13_Libraries_and_Scripts/1_Imports_A03_t05: fail
+Language/13_Libraries_and_Scripts/1_Imports_A03_t07: fail
+Language/13_Libraries_and_Scripts/1_Imports_A03_t08: fail
+Language/13_Libraries_and_Scripts/1_Imports_A03_t09: fail
+Language/13_Libraries_and_Scripts/1_Imports_A03_t10: fail
+Language/13_Libraries_and_Scripts/1_Imports_A03_t22: fail
+Language/13_Libraries_and_Scripts/1_Imports_A03_t25: fail
+Language/13_Libraries_and_Scripts/1_Imports_A03_t27: fail
+Language/13_Libraries_and_Scripts/1_Imports_A03_t28: fail
+Language/13_Libraries_and_Scripts/1_Imports_A03_t29: fail
+Language/13_Libraries_and_Scripts/1_Imports_A03_t30: fail
+Language/13_Libraries_and_Scripts/1_Imports_A03_t42: fail
+Language/13_Libraries_and_Scripts/1_Imports_A03_t45: fail
+Language/13_Libraries_and_Scripts/1_Imports_A03_t48: fail
+Language/13_Libraries_and_Scripts/1_Imports_A03_t49: fail
+Language/13_Libraries_and_Scripts/1_Imports_A03_t50: fail
+Language/13_Libraries_and_Scripts/1_Imports_A03_t62: fail
+Language/13_Libraries_and_Scripts/1_Imports_A03_t65: fail
+Language/13_Libraries_and_Scripts/1_Imports_A03_t68: fail
+Language/13_Libraries_and_Scripts/1_Imports_A03_t69: fail
+Language/13_Libraries_and_Scripts/1_Imports_A03_t70: fail
+Language/13_Libraries_and_Scripts/1_Imports_A04_t03: fail
+Language/13_Libraries_and_Scripts/1_Imports_A05_t01: fail
+Language/13_Libraries_and_Scripts/2_Exports_A04_t02: fail
+Language/13_Libraries_and_Scripts/2_Exports_A04_t03: fail
+Language/13_Libraries_and_Scripts/2_Exports_A05_t01: fail
+Language/13_Libraries_and_Scripts/5_URIs_A01_t24: fail
+Language/13_Libraries_and_Scripts/5_URIs_A01_t25: fail
+Language/14_Types/3_Type_Declarations/1_Typedef_A07_t01: fail
+Language/14_Types/3_Type_Declarations/1_Typedef_A07_t02: fail
+Language/14_Types/3_Type_Declarations/1_Typedef_A07_t03: fail
+Language/14_Types/3_Type_Declarations/1_Typedef_A07_t04: fail
+Language/15_Reference/1_Lexical_Rules/1_Reserved_Words_A40_t04: fail
+Language/15_Reference/1_Lexical_Rules_A02_t06: fail
+LibTest/core/double/ceil_A01_t05: fail
+LibTest/core/double/floor_A01_t05: fail
+
+
+[ $runtime == drt && $compiler == none ]
+*: Skip
diff --git a/tests/co19/co19-dart2dart.status b/tests/co19/co19-dart2dart.status
index 78dc0f9..b2502fa 100644
--- a/tests/co19/co19-dart2dart.status
+++ b/tests/co19/co19-dart2dart.status
@@ -560,6 +560,11 @@
LibTest/async/Future/Future.immediateError_A01_t01: Fail # No AsyncError anymore. Issue 407
LibTest/async/Future/catchError_A02_t01: Fail # No AsyncError anymore. Issue 407
+LibTest/async/Completer/complete_A01_t03: Fail # Completer is now asynchronous. Issue TODO
+LibTest/async/Completer/complete_A01_t04: Fail # Completer is now asynchronous. Issue TODO
+LibTest/async/Future/then_A01_t01: Fail # Completer is now asynchronous. Issue TODO
+LibTest/async/Future/then_A01_t04: Fail # Completer is now asynchronous. Issue TODO
+
Language/11_Expressions/05_Strings/1_String_Interpolation_A03_t02: Fail # Issue 397
Language/11_Expressions/05_Strings/1_String_Interpolation_A04_t02: Fail # Issue 397
Language/11_Expressions/11_Instance_Creation/1_New_A08_t01: Fail # Issue 397
diff --git a/tests/co19/co19-dart2js.status b/tests/co19/co19-dart2js.status
index 3e46304..9c16e3d 100644
--- a/tests/co19/co19-dart2js.status
+++ b/tests/co19/co19-dart2js.status
@@ -537,6 +537,11 @@
LibTest/async/Future/Future.immediateError_A01_t01: Fail # No AsyncError anymore. Issue 407
LibTest/async/Future/catchError_A02_t01: Fail # No AsyncError anymore. Issue 407
+LibTest/async/Completer/complete_A01_t03: Fail # Completer is now asynchronous. Issue TODO
+LibTest/async/Completer/complete_A01_t04: Fail # Completer is now asynchronous. Issue TODO
+LibTest/async/Future/then_A01_t01: Fail # Completer is now asynchronous. Issue TODO
+LibTest/async/Future/then_A01_t04: Fail # Completer is now asynchronous. Issue TODO
+
Language/11_Expressions/14_Function_Invocation/4_Function_Expression_Invocation_A01_t02: Fail, OK # co19 issue 409
Language/11_Expressions/15_Method_Invocation/1_Ordinary_Invocation_A05_t02: Fail, OK # co19 issue 409
Language/11_Expressions/15_Method_Invocation/1_Ordinary_Invocation_A05_t03: Fail, OK # co19 issue 409
diff --git a/tests/co19/co19-runtime.status b/tests/co19/co19-runtime.status
index 4e265b3..99470a9 100644
--- a/tests/co19/co19-runtime.status
+++ b/tests/co19/co19-runtime.status
@@ -484,6 +484,11 @@
LibTest/async/Future/Future.immediateError_A01_t01: Fail # No AsyncError anymore. Issue 407
LibTest/async/Future/catchError_A02_t01: Fail # No AsyncError anymore. Issue 407
+LibTest/async/Completer/complete_A01_t03: Fail # Completer is now asynchronous. Issue TODO
+LibTest/async/Completer/complete_A01_t04: Fail # Completer is now asynchronous. Issue TODO
+LibTest/async/Future/then_A01_t01: Fail # Completer is now asynchronous. Issue TODO
+LibTest/async/Future/then_A01_t04: Fail # Completer is now asynchronous. Issue TODO
+
[ $compiler == none && $runtime == vm && $checked ]
LibTest/core/Set/intersection_A01_t01: Fail # issue 390
LibTest/core/Set/intersection_A01_t02: Fail # issue 390
diff --git a/tests/co19/test_config.dart b/tests/co19/test_config.dart
index 1ea56de..0392975 100644
--- a/tests/co19/test_config.dart
+++ b/tests/co19/test_config.dart
@@ -14,7 +14,8 @@
: super(configuration,
"co19",
new Path("tests/co19/src"),
- ["tests/co19/co19-compiler.status",
+ ["tests/co19/co19-analyzer.status",
+ "tests/co19/co19-compiler.status",
"tests/co19/co19-runtime.status",
"tests/co19/co19-dart2dart.status",
"tests/co19/co19-dart2js.status"]);
diff --git a/tests/compiler/dart2js/deprecated_features_test.dart b/tests/compiler/dart2js/deprecated_features_test.dart
index 588e77f..3ca046e 100644
--- a/tests/compiler/dart2js/deprecated_features_test.dart
+++ b/tests/compiler/dart2js/deprecated_features_test.dart
@@ -31,7 +31,7 @@
if (uri.scheme != "main") return dummy.provider(uri);
String source = TEST_SOURCE[uri.path];
Expect.isNotNull(source);
- return (new Completer<String>()..complete(source)).future;
+ return new Future<String>.value(source);
}
String code = deprecatedFutureValue(
diff --git a/tests/compiler/dart2js/mock_compiler.dart b/tests/compiler/dart2js/mock_compiler.dart
index 1aa2277..877c3d1 100644
--- a/tests/compiler/dart2js/mock_compiler.dart
+++ b/tests/compiler/dart2js/mock_compiler.dart
@@ -305,7 +305,8 @@
resolverVisitor() {
Element mockElement =
- new ElementX(buildSourceString(''), ElementKind.FUNCTION, mainApp);
+ new ElementX(buildSourceString(''), ElementKind.FUNCTION,
+ mainApp.entryCompilationUnit);
ResolverVisitor visitor =
new ResolverVisitor(this, mockElement,
new CollectingTreeElements(mockElement));
diff --git a/tests/corelib/corelib.status b/tests/corelib/corelib.status
index cd29121c..3008f2e 100644
--- a/tests/corelib/corelib.status
+++ b/tests/corelib/corelib.status
@@ -89,6 +89,10 @@
symbol_test/02: Fail
symbol_test/03: Fail
+[ $compiler == dartanalyzer ]
+int_parse_radix_test: fail
+list_insert_test: fail
+list_removeat_test: fail
[ $arch == arm ]
*: Skip
diff --git a/tests/html/element_classes_test.dart b/tests/html/element_classes_test.dart
index 83bb86c..3c628b9 100644
--- a/tests/html/element_classes_test.dart
+++ b/tests/html/element_classes_test.dart
@@ -19,6 +19,11 @@
Element makeElementWithClasses() =>
new Element.html('<div class="foo bar baz"></div>');
+ Element makeListElement() =>
+ new Element.html('<ul class="foo bar baz">'
+ '<li class="quux qux"><li class="meta">'
+ '<li class="classy lassy"><li class="qux lassy"></ul>');
+
Set<String> makeClassSet() => makeElementWithClasses().classes;
Set<String> extractClasses(Element el) {
@@ -53,9 +58,7 @@
test('forEach', () {
final classes = <String>[];
- // TODO: Change to this when Issue 3484 is fixed.
- // makeClassSet().forEach(classes.add);
- makeClassSet().forEach((c) => classes.add(c));
+ makeClassSet().forEach(classes.add);
expect(classes, orderedEquals(['foo', 'bar', 'baz']));
});
@@ -177,4 +180,141 @@
classes.toggle('baz');
expect(classes, orderedEquals(['foo', 'bar', 'aardvark', 'baz']));
});
+
+ Element listElement;
+
+ ElementList listElementSetup() {
+ listElement = makeListElement();
+ document.documentElement.children.add(listElement);
+ return document.queryAll('li');
+ }
+
+ test('listClasses=', () {
+ var elements = listElementSetup();
+ elements.classes = ['foo', 'qux'];
+ elements = document.queryAll('li');
+ for (Element e in elements) {
+ expect(e.classes, orderedEquals(['foo', 'qux']));
+ expect(extractClasses(e), orderedEquals(['foo', 'qux']));
+ }
+
+ elements.classes = [];
+ for (Element e in elements) {
+ expect(e.classes, []);
+ }
+ document.documentElement.children.remove(listElement);
+ });
+
+ test('listMap', () {
+ var elements = listElementSetup();
+ expect(elements.classes.map((c) => c.toUpperCase()).toList(),
+ unorderedEquals(['QUX', 'QUUX', 'META', 'CLASSY', 'LASSY']));
+ document.documentElement.children.remove(listElement);
+ });
+
+ test('listContains', () {
+ var elements = listElementSetup();
+ expect(elements.classes.contains('lassy'), isTrue);
+ expect(elements.classes.contains('foo'), isFalse);
+ document.documentElement.children.remove(listElement);
+ });
+
+
+ test('listAdd', () {
+ var elements = listElementSetup();
+ elements.classes.add('lassie');
+ expect(elements.classes,
+ unorderedEquals(['lassie', 'qux', 'quux', 'meta', 'classy', 'lassy']));
+ for (Element e in elements) {
+ expect(e.classes, anyOf(unorderedEquals(['quux', 'qux', 'lassie']),
+ unorderedEquals(['meta', 'lassie']),
+ unorderedEquals(['classy', 'lassy', 'lassie']),
+ unorderedEquals(['qux', 'lassy', 'lassie'])));
+ }
+ document.documentElement.children.remove(listElement);
+ });
+
+ test('listRemove', () {
+ var elements = listElementSetup();
+ expect(elements.classes.remove('lassi'), isFalse);
+ expect(elements.classes,
+ unorderedEquals(['qux', 'quux', 'meta', 'classy', 'lassy']));
+ for (Element e in elements) {
+ expect(e.classes, anyOf(unorderedEquals(['quux', 'qux']),
+ unorderedEquals(['meta']), unorderedEquals(['classy', 'lassy']),
+ unorderedEquals(['qux', 'lassy'])));
+ }
+
+ expect(elements.classes.remove('qux'), isTrue);
+ expect(elements.classes,
+ unorderedEquals(['quux', 'meta', 'classy', 'lassy']));
+ for (Element e in elements) {
+ expect(e.classes, anyOf(unorderedEquals(['quux']),
+ unorderedEquals(['meta']), unorderedEquals(['classy', 'lassy']),
+ unorderedEquals(['lassy'])));
+ }
+ document.documentElement.children.remove(listElement);
+ });
+
+ test('listToggle', () {
+ var elements = listElementSetup();
+ elements.classes.toggle('qux');
+ expect(elements.classes,
+ unorderedEquals(['qux', 'quux', 'meta', 'classy', 'lassy']));
+ for (Element e in elements) {
+ expect(e.classes, anyOf(unorderedEquals(['quux']),
+ unorderedEquals(['meta', 'qux']), unorderedEquals(['classy', 'lassy',
+ 'qux']), unorderedEquals(['lassy'])));
+ }
+ document.documentElement.children.remove(listElement);
+ });
+
+ test('listAddAll', () {
+ var elements = listElementSetup();
+ elements.classes.addAll(['qux', 'lassi', 'sassy']);
+ expect(elements.classes,
+ unorderedEquals(['qux', 'quux', 'meta', 'classy', 'lassy', 'sassy',
+ 'lassi']));
+ for (Element e in elements) {
+ expect(e.classes, anyOf(
+ unorderedEquals(['quux', 'qux', 'lassi', 'sassy']),
+ unorderedEquals(['meta', 'qux', 'lassi', 'sassy']),
+ unorderedEquals(['classy', 'lassy', 'qux', 'lassi','sassy']),
+ unorderedEquals(['lassy', 'qux', 'lassi', 'sassy'])));
+ }
+ document.documentElement.children.remove(listElement);
+ });
+
+ test('listRemoveAll', () {
+ var elements = listElementSetup();
+ elements.classes.removeAll(['qux', 'lassy', 'meta']);
+ expect(elements.classes,
+ unorderedEquals(['quux','classy']));
+ for (Element e in elements) {
+ expect(e.classes, anyOf(unorderedEquals(['quux']),
+ unorderedEquals([]), unorderedEquals(['classy'])));
+ }
+ document.documentElement.children.remove(listElement);
+ });
+
+ test('listToggleAll', () {
+ var elements = listElementSetup();
+ elements.classes.toggleAll(['qux', 'meta', 'mornin']);
+ expect(elements.classes,
+ unorderedEquals(['qux', 'quux', 'meta', 'classy', 'lassy', 'mornin']));
+ for (Element e in elements) {
+ expect(e.classes, anyOf(unorderedEquals(['quux', 'meta', 'mornin']),
+ unorderedEquals(['qux', 'mornin']),
+ unorderedEquals(['classy', 'lassy', 'qux', 'mornin', 'meta']),
+ unorderedEquals(['lassy', 'mornin', 'meta'])));
+ }
+ document.documentElement.children.remove(listElement);
+ });
+
+ test('listContainsAll', () {
+ var elements = listElementSetup();
+ expect(elements.classes.containsAll(['qux', 'meta', 'mornin']), isFalse);
+ expect(elements.classes.containsAll(['qux', 'lassy', 'classy']), isTrue);
+ document.documentElement.children.remove(listElement);
+ });
}
diff --git a/tests/html/html.status b/tests/html/html.status
index 6282522..8d10943 100644
--- a/tests/html/html.status
+++ b/tests/html/html.status
@@ -12,6 +12,8 @@
[ $compiler == none && ($runtime == drt || $runtime == dartium) ]
# postMessage in dartium always transfers the typed array buffer, never a view
postmessage_structured_test/typed_arrays: Fail
+async_test: Timeout # http://dartbug.com/10470
+dom_isolates_test: Timeout # http://dartbug.com/10470
[ $compiler == none && $runtime == drt && $system == windows ]
worker_test/functional: Pass, Crash # Issue 9929.
@@ -175,6 +177,7 @@
media_stream_test/supported_media: Fail
mutationobserver_test/supported: Fail
notifications_test/supported: Fail
+range_test/supported: Fail
rtc_test/supported: Fail
shadow_dom_test/supported: Fail
speechrecognition_test/supported: Fail
@@ -365,4 +368,5 @@
js_interop_2_test: Fail, OK # Test cannot run under CSP restrictions.
js_interop_3_test: Skip # Test cannot run under CSP restrictions (times out).
postmessage_structured_test: Skip # Test cannot run under CSP restrictions (times out).
+safe_dom_test: Skip # Test cannot run under CSP restrictions (times out).
shadow_dom_layout_test: Fail, OK # Test cannot run under CSP restrictions.
diff --git a/tests/html/location_test.dart b/tests/html/location_test.dart
index e1f0fb8..88e3f2d 100644
--- a/tests/html/location_test.dart
+++ b/tests/html/location_test.dart
@@ -2,6 +2,7 @@
import '../../pkg/unittest/lib/unittest.dart';
import '../../pkg/unittest/lib/html_config.dart';
import 'dart:html';
+import 'dart:uri';
main() {
useHtmlConfiguration();
@@ -17,4 +18,17 @@
var h = location.hash;
expect(h, '#hello');
});
+
+ test('location.origin', () {
+ var origin = window.location.origin;
+
+ // We build up the origin from Uri, then make sure that it matches.
+ var uri = new Uri(window.location.href);
+ var reconstructedOrigin = '${uri.scheme}://${uri.domain}';
+ if (uri.port != 0) {
+ reconstructedOrigin = '$reconstructedOrigin:${uri.port}';
+ }
+
+ expect(origin, reconstructedOrigin);
+ });
}
diff --git a/tests/html/range_test.dart b/tests/html/range_test.dart
new file mode 100644
index 0000000..3792afd
--- /dev/null
+++ b/tests/html/range_test.dart
@@ -0,0 +1,29 @@
+library range_test;
+
+import 'dart:html';
+import '../../pkg/unittest/lib/unittest.dart';
+import '../../pkg/unittest/lib/html_individual_config.dart';
+
+main() {
+ useHtmlIndividualConfiguration();
+
+ group('supported', () {
+ test('supports_createContextualFragment', () {
+ expect(Range.supportsCreateContextualFragment, isTrue);
+ });
+ });
+
+ group('functional', () {
+ test('supported works', () {
+ var range = new Range();
+ range.selectNode(document.body);
+
+ var expectation = Range.supportsCreateContextualFragment ?
+ returnsNormally : throws;
+
+ expect(() {
+ range.createContextualFragment('<div></div>');
+ }, expectation);
+ });
+ });
+}
diff --git a/tests/html/safe_dom_test.dart b/tests/html/safe_dom_test.dart
new file mode 100644
index 0000000..5e7ae35
--- /dev/null
+++ b/tests/html/safe_dom_test.dart
@@ -0,0 +1,98 @@
+library safe_dom_test;
+
+import 'dart:async';
+import 'dart:html';
+import '../../pkg/unittest/lib/unittest.dart';
+import '../../pkg/unittest/lib/html_config.dart';
+
+main() {
+ useHtmlConfiguration();
+
+ // Checks to see if any illegal properties were set via script.
+ var checkerScript = '''
+ window.addEventListener('message', function(e) {
+ if (e.data == 'check_unsafe') {
+ if (window.unsafe_value) {
+ window.postMessage('unsafe_check_failed', '*');
+ } else {
+ window.postMessage('unsafe_check_passed', '*');
+ }
+ //window.alert('checking!');
+ }
+ }, false);
+ ''';
+
+ var script = new ScriptElement();
+ script.text = checkerScript;
+ document.body.append(script);
+
+ var unsafeString =
+ '<img src="_.png" onerror="javascript:window.unsafe_value=1;" crap="1"/>';
+
+ test('Safe DOM', () {
+ var fragment = createContextualFragment(unsafeString);
+
+ expect(isSafe(), completion(true),
+ reason: 'Expected no unsafe code executed.');
+ });
+
+ // Make sure that scripts did get executed, so we know our detection works.
+ test('Unsafe Execution', () {
+ var div = new DivElement();
+ div.innerHtml = unsafeString;
+ // Crashing DRT ??
+ // var fragment = createContextualFragment(unsafeString);
+ // div.append(fragment);
+ // document.body.append(div)
+
+ expect(isSafe(), completion(false),
+ reason: 'Expected unsafe code was executed.');
+ });
+
+ test('Validity', () {
+ var fragment = createContextualFragment('<span>content</span>');
+ var div = new DivElement();
+ div.append(fragment);
+
+ expect(div.nodes.length, 1);
+ expect(div.nodes[0] is SpanElement, isTrue);
+ });
+}
+
+DocumentFragment createContextualFragment(String html, [String contextTag]) {
+ var doc = document.implementation.createHtmlDocument('');
+
+ var contextElement;
+ if (contextTag != null) {
+ contextElement = doc.$dom_createElement(contextTag);
+ } else {
+ contextElement = doc.body;
+ }
+
+ if (Range.supportsCreateContextualFragment) {
+ var range = doc.$dom_createRange();
+ range.selectNode(contextElement);
+ return range.createContextualFragment(html);
+ } else {
+ contextElement.innerHtml = html;
+ var fragment = new DocumentFragment();;
+ while (contextElement.$dom_firstChild != null) {
+ fragment.append(contextElement.$dom_firstChild);
+ }
+ return fragment;
+ }
+}
+
+// Delay to wait for the image load to fail.
+const Duration imageLoadDelay = const Duration(milliseconds: 500);
+
+Future<bool> isSafe() {
+ return new Future.delayed(imageLoadDelay).then((_) {
+ window.postMessage('check_unsafe', '*');
+ }).then((_) {
+ return window.onMessage.where(
+ (e) => e.data.startsWith('unsafe_check')).first;
+ }).then((e) {
+ return e.data == 'unsafe_check_passed';
+ });
+}
diff --git a/tests/html/streams_test.dart b/tests/html/streams_test.dart
index b0feb5e..7a3b2ff 100644
--- a/tests/html/streams_test.dart
+++ b/tests/html/streams_test.dart
@@ -112,7 +112,7 @@
helper.pulse();
expect(callCount, 2);
- var completer = new Completer<int>();
+ var completer = new Completer<int>.sync();
subscription.pause(completer.future);
helper.pulse();
expect(callCount, 2);
diff --git a/tests/html/websql_test.dart b/tests/html/websql_test.dart
index cac79de..2c77b6a 100644
--- a/tests/html/websql_test.dart
+++ b/tests/html/websql_test.dart
@@ -12,7 +12,7 @@
}
Future<SqlTransaction> transaction(SqlDatabase db) {
- final completer = new Completer<SqlTransaction>();
+ final completer = new Completer<SqlTransaction>.sync();
db.transaction((SqlTransaction transaction) {
completer.complete(transaction);
@@ -25,7 +25,7 @@
Future<SqlResultSet> createTable(SqlTransaction transaction, String tableName,
String columnName) {
- final completer = new Completer<SqlResultSet>();
+ final completer = new Completer<SqlResultSet>.sync();
final sql = 'CREATE TABLE $tableName ($columnName)';
transaction.executeSql(sql, [],
@@ -41,7 +41,7 @@
Future<SqlResultSet> insert(SqlTransaction transaction, String tableName,
String columnName, value) {
- final completer = new Completer<SqlResultSet>();
+ final completer = new Completer<SqlResultSet>.sync();
final sql = 'INSERT INTO $tableName ($columnName) VALUES (?)';
transaction.executeSql(sql, [value],
@@ -56,7 +56,7 @@
}
Future<SqlResultSet> queryTable(SqlTransaction transaction, String tableName) {
- final completer = new Completer<SqlResultSet>();
+ final completer = new Completer<SqlResultSet>.sync();
final sql = 'SELECT * FROM $tableName';
transaction.executeSql(sql, [],
@@ -72,7 +72,7 @@
Future<SqlResultSet> dropTable(SqlTransaction transaction, String tableName,
[bool ignoreFailure = false]) {
- final completer = new Completer<SqlResultSet>();
+ final completer = new Completer<SqlResultSet>.sync();
final sql = 'DROP TABLE $tableName';
transaction.executeSql(sql, [],
diff --git a/tests/isolate/isolate.status b/tests/isolate/isolate.status
index 89c72e7..b0cd54b 100644
--- a/tests/isolate/isolate.status
+++ b/tests/isolate/isolate.status
@@ -36,6 +36,15 @@
isolate2_negative_test: Pass
compute_this_script_browser_test: Pass
+[ $compiler == dartanalyzer ]
+isolate2_negative_test: fail
+isolate3_negative_test: fail
+isolate_import_negative_test: fail
+isolate_negative_test: fail
+spawn_function_negative_test: fail
+spawn_uri_negative_test: fail
+spawn_uri_vm_negative_test: fail
+unresolved_ports_negative_test: fail
[ $compiler == dart2js && ($runtime == d8 || $jscl) ]
illegal_msg_stream_test: Fail # Issue 6750
diff --git a/tests/language/allocation_sinking_vm_test.dart b/tests/language/allocation_sinking_vm_test.dart
new file mode 100644
index 0000000..d7bb7b9
--- /dev/null
+++ b/tests/language/allocation_sinking_vm_test.dart
@@ -0,0 +1,132 @@
+// Copyright (c) 2013, 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.
+// Test allocation sinking optimization.
+
+import 'package:expect/expect.dart';
+
+class Point {
+ var x, y;
+
+ Point(this.x, this.y);
+
+ operator * (other) {
+ return x * other.x + y * other.y;
+ }
+}
+
+class C {
+ var p;
+ C(this.p);
+}
+
+class D {
+ var p;
+ D(this.p);
+}
+
+// Class that is used to capture materialized Point object with * operator.
+class F {
+ var p;
+ var val;
+
+ F(this.p);
+
+ operator * (other) {
+ Expect.isTrue(other is Point);
+ Expect.equals(42.0, other.x);
+ Expect.equals(0.5, other.y);
+
+ if (val == null) {
+ val = other;
+ } else {
+ Expect.isTrue(identical(val, other));
+ }
+
+ return this.p * other;
+ }
+}
+
+test1(c, x, y) {
+ var a = new Point(x - 0.5, y + 0.5);
+ var b = new Point(x + 0.5, y + 0.8);
+ var d = new Point(c.p * a, c.p * b);
+ return d * d;
+}
+
+effects() {
+ // This function should not be inlinable.
+ try { } catch (e) { }
+}
+
+testForwardingThroughEffects(c, x, y) {
+ var a = new Point(x - 0.5, y + 0.5);
+ var b = new Point(x - 0.5, y - 0.8);
+ var d = new Point(c.p * a, c.p * b);
+ // Effects can't affect neither a, b, nor d because they do not escape.
+ effects();
+ effects();
+ return ((a == null) ? 0.0 : 0.1) + (d * d);
+}
+
+testIdentity(x) {
+ var y = new Point(42.0, 0.5);
+ var z = y;
+ return x * y + x * z;
+}
+
+class PointP<T> {
+ var x, y;
+
+ PointP(this.x, this.y);
+
+ operator * (other) {
+ return x * other.x + y * other.y;
+ }
+}
+
+foo2() => new PointP<int>(1, 3) * new PointP<num>(5, 6);
+
+main() {
+ var c = new C(new Point(0.1, 0.2));
+
+ // Compute initial values.
+ final x0 = test1(c, 11.11, 22.22);
+ final y0 = testForwardingThroughEffects(c, 11.11, 22.22);
+ final z0 = testIdentity(c.p);
+
+ // Force optimization.
+ for (var i = 0; i < 10000; i++) {
+ test1(c, i.toDouble(), i.toDouble());
+ testForwardingThroughEffects(c, i.toDouble(), i.toDouble());
+ testIdentity(c.p);
+ foo2();
+ }
+
+ // Test returned value after optimization.
+ final x1 = test1(c, 11.11, 22.22);
+ final y1 = testForwardingThroughEffects(c, 11.11, 22.22);
+
+ // Test returned value after deopt.
+ final x2 = test1(new D(c.p), 11.11, 22.22);
+ final y2 = testForwardingThroughEffects(new D(c.p), 11.11, 22.22);
+
+ Expect.equals(6465, (x0 * 100).floor());
+ Expect.equals(6465, (x1 * 100).floor());
+ Expect.equals(6465, (x2 * 100).floor());
+ Expect.equals(x0, x1);
+ Expect.equals(x0, x2);
+
+ Expect.equals(6008, (y0 * 100).floor());
+ Expect.equals(6008, (y1 * 100).floor());
+ Expect.equals(6008, (y2 * 100).floor());
+ Expect.equals(y0, y1);
+ Expect.equals(y0, y2);
+
+ // Test that identity of materialized objects is preserved correctly and
+ // no copies are materialized.
+ final z1 = testIdentity(c.p);
+ final z2 = testIdentity(new F(c.p));
+ Expect.equals(z0, z1);
+ Expect.equals(z0, z2);
+}
\ No newline at end of file
diff --git a/tests/language/language.status b/tests/language/language.status
index 879ef11..6e939ca 100644
--- a/tests/language/language.status
+++ b/tests/language/language.status
@@ -555,6 +555,411 @@
invocation_mirror_test: Fail, OK # hardcoded names.
super_call4_test: Fail, OK # hardcoded names.
+[ $compiler == dartanalyzer ]
+abstract_factory_constructor_test/00: fail
+assign_instance_method_negative_test: fail
+assign_static_type_test/01: fail
+assign_static_type_test/02: fail
+assign_static_type_test/03: fail
+assign_static_type_test/04: fail
+assign_static_type_test/05: fail
+assign_static_type_test/06: fail
+bad_initializer2_negative_test: fail
+bad_named_constructor_negative_test: fail
+bad_named_parameters2_test: fail
+bad_named_parameters_test: fail
+bad_override_test/01: fail
+bad_override_test/02: fail
+bad_override_test/03: fail
+bad_override_test/04: fail
+bad_override_test/05: fail
+black_listed_test/11: fail
+body_less_constructor_wrong_arg_negative_test: fail
+built_in_identifier_prefix_test: fail
+call_constructor_on_unresolvable_class_test/01: fail
+call_constructor_on_unresolvable_class_test/02: fail
+call_constructor_on_unresolvable_class_test/03: fail
+call_constructor_on_unresolvable_class_test/07: fail
+call_non_method_field_test/01: fail
+call_non_method_field_test/02: fail
+call_nonexistent_static_test/01: fail
+call_nonexistent_static_test/02: fail
+call_nonexistent_static_test/03: fail
+call_nonexistent_static_test/04: fail
+call_nonexistent_static_test/05: fail
+call_nonexistent_static_test/06: fail
+call_nonexistent_static_test/07: fail
+call_nonexistent_static_test/08: fail
+call_nonexistent_static_test/09: fail
+call_nonexistent_static_test/10: fail
+call_through_getter_test: fail
+callable_test/00: fail
+callable_test/01: fail
+cast_test/04: fail
+cast_test/05: fail
+class_cycle_negative_test: fail
+class_cycle_test/00: fail
+class_cycle_test/01: fail
+class_cycle_test/03: fail
+class_literal_test/02: fail
+class_literal_test/05: fail
+class_literal_test/07: fail
+class_literal_test/10: fail
+class_literal_test/11: fail
+class_literal_test/12: fail
+class_literal_test/14: fail
+class_literal_test/17: fail
+class_literal_test/18: fail
+class_literal_test/19: fail
+class_literal_test/22: fail
+class_literal_test/23: fail
+class_literal_test/24: fail
+class_literal_test/27: fail
+class_literal_test/28: fail
+class_literal_test/29: fail
+closure_call_wrong_argument_count_negative_test: fail
+compile_time_constant10_test/none: fail
+compile_time_constant8_test: fail
+compile_time_constant_arguments_test/01: fail
+compile_time_constant_arguments_test/02: fail
+compile_time_constant_arguments_test/03: fail
+compile_time_constant_arguments_test/04: fail
+compile_time_constant_arguments_test/05: fail
+compile_time_constant_arguments_test/06: fail
+compile_time_constant_b_test: fail
+compile_time_constant_checked2_test/01: fail
+compile_time_constant_checked2_test/02: fail
+compile_time_constant_checked2_test/03: fail
+compile_time_constant_checked2_test/04: fail
+compile_time_constant_checked2_test/05: fail
+compile_time_constant_checked2_test/06: fail
+compile_time_constant_checked3_test/01: fail
+compile_time_constant_checked3_test/02: fail
+compile_time_constant_checked3_test/03: fail
+compile_time_constant_checked3_test/04: fail
+compile_time_constant_checked3_test/05: fail
+compile_time_constant_checked3_test/06: fail
+compile_time_constant_checked_test/01: fail
+compile_time_constant_checked_test/02: fail
+compile_time_constant_checked_test/03: fail
+compile_time_constant_d_test: fail
+compile_time_constant_e_test: fail
+compile_time_constant_test/02: fail
+const_counter_negative_test: fail
+const_factory_negative_test: fail
+const_optional_args_negative_test: fail
+const_syntax_test/06: fail
+constructor2_negative_test: fail
+constructor3_negative_test: fail
+constructor_call_wrong_argument_count_negative_test: fail
+constructor_name_test/01: fail
+constructor_name_test/02: fail
+constructor_name_test/03: fail
+constructor_negative_test: fail
+constructor_redirect1_negative_test: fail
+constructor_redirect2_negative_test: fail
+constructor_redirect2_test/02: fail
+constructor_redirect2_test/03: fail
+constructor_redirect2_test/04: fail
+constructor_redirect_test/01: fail
+constructor_return_with_arrow_negative_test: fail
+constructor_return_with_init_and_arrow_negative_test: fail
+constructor_setter_negative_test: fail
+cyclic_constructor_test/01: fail
+cyclic_type_variable_test/01: fail
+cyclic_type_variable_test/02: fail
+cyclic_type_variable_test/03: fail
+cyclic_type_variable_test/04: fail
+default_factory2_test/01: fail
+default_implementation2_test: fail
+duplicate_constructor_test/01: fail
+duplicate_export_negative_test: fail
+dynamic2_test/00: fail
+dynamic_field_test: fail
+f_bounded_quantification_test/01: fail
+f_bounded_quantification_test/02: fail
+factory2_negative_test: fail
+factory2_test: fail
+factory5_test/00: fail
+factory_negative_test: fail
+factory_redirection2_test/01: fail
+factory_redirection_test/04: fail
+factory_redirection_test/07: fail
+fauxverride_test/03: fail
+fauxverride_test/05: fail
+field_decl_missing_var_type_test/01: fail
+field_method4_negative_test: fail
+field_override_test/01: fail
+field_override_test/02: fail
+field_type_check_test/01: fail
+final_for_in_variable_test/01: fail
+final_param_negative_test: fail
+final_var_negative_test: fail
+final_variable_assignment_test/01: fail
+final_variable_assignment_test/02: fail
+final_variable_assignment_test/03: fail
+final_variable_assignment_test/04: fail
+first_class_types_constants_test: fail
+for_in2_test: fail
+function_type_alias5_test/00: fail
+function_type_alias5_test/01: fail
+function_type_alias5_test/02: fail
+getter_no_setter2_test/00: fail
+getter_no_setter2_test/02: fail
+getter_no_setter2_test/03: fail
+getter_no_setter_test/00: fail
+getter_no_setter_test/01: fail
+getter_no_setter_test/02: fail
+getters_setters2_test/01: fail
+getters_setters2_test/02: fail
+getters_setters_type_test/01: fail
+implicit_scope_test: fail
+implicit_this_test/01: fail
+implicit_this_test/04: fail
+implicit_this_test/none: fail
+import_combinators_negative_test: fail
+import_private_test/01: fail
+inst_field_initializer1_negative_test: fail
+instance_call_wrong_argument_count_negative_test: fail
+instance_method2_negative_test: fail
+instance_method_negative_test: fail
+instantiate_type_variable_negative_test: fail
+interface_cycle_negative_test: fail
+interface_inherit_field_test: fail
+interface_static_non_final_fields_negative_test: fail
+interface_test/00: fail
+internal_library_test/01: fail
+is_not_class2_negative_test: fail
+issue1363_test: fail
+library1_negative_test: fail
+library_juxtaposition_test: fail
+licm_test: fail
+list_literal1_negative_test: fail
+list_literal_syntax_test/01: fail
+list_literal_syntax_test/02: fail
+list_literal_syntax_test/03: fail
+list_literal_syntax_test/05: fail
+map_literal1_negative_test: fail
+map_literal2_test: fail
+map_literal3_test: fail
+method_override2_test/00: fail
+method_override2_test/01: fail
+method_override2_test/02: fail
+method_override2_test/03: fail
+mixin_cyclic_test/01: fail
+mixin_illegal_constructor_test/01: fail
+mixin_illegal_constructor_test/02: fail
+mixin_illegal_constructor_test/03: fail
+mixin_illegal_constructor_test/04: fail
+mixin_illegal_constructor_test/05: fail
+mixin_illegal_constructor_test/06: fail
+mixin_illegal_constructor_test/07: fail
+mixin_illegal_constructor_test/08: fail
+mixin_illegal_constructor_test/09: fail
+mixin_illegal_constructor_test/10: fail
+mixin_illegal_constructor_test/11: fail
+mixin_illegal_constructor_test/12: fail
+mixin_illegal_constructor_test/13: fail
+mixin_illegal_constructor_test/14: fail
+mixin_illegal_constructor_test/15: fail
+mixin_illegal_constructor_test/16: fail
+mixin_illegal_cycles_test/01: fail
+mixin_illegal_cycles_test/02: fail
+mixin_illegal_cycles_test/03: fail
+mixin_illegal_cycles_test/04: fail
+mixin_illegal_cycles_test/05: fail
+mixin_illegal_cycles_test/06: fail
+mixin_illegal_super_use_test/01: fail
+mixin_illegal_super_use_test/02: fail
+mixin_illegal_super_use_test/03: fail
+mixin_illegal_super_use_test/04: fail
+mixin_illegal_super_use_test/05: fail
+mixin_illegal_super_use_test/06: fail
+mixin_illegal_super_use_test/07: fail
+mixin_illegal_super_use_test/08: fail
+mixin_illegal_super_use_test/09: fail
+mixin_illegal_super_use_test/10: fail
+mixin_illegal_super_use_test/11: fail
+mixin_illegal_superclass_test/01: fail
+mixin_illegal_superclass_test/02: fail
+mixin_illegal_superclass_test/03: fail
+mixin_illegal_superclass_test/04: fail
+mixin_illegal_superclass_test/05: fail
+mixin_illegal_superclass_test/06: fail
+mixin_illegal_superclass_test/07: fail
+mixin_illegal_superclass_test/08: fail
+mixin_illegal_superclass_test/09: fail
+mixin_illegal_superclass_test/10: fail
+mixin_illegal_superclass_test/11: fail
+mixin_illegal_superclass_test/12: fail
+mixin_illegal_superclass_test/13: fail
+mixin_illegal_superclass_test/14: fail
+mixin_illegal_superclass_test/15: fail
+mixin_illegal_superclass_test/16: fail
+mixin_illegal_superclass_test/17: fail
+mixin_illegal_superclass_test/18: fail
+mixin_illegal_superclass_test/19: fail
+mixin_illegal_superclass_test/20: fail
+mixin_illegal_superclass_test/21: fail
+mixin_illegal_superclass_test/22: fail
+mixin_illegal_superclass_test/23: fail
+mixin_illegal_superclass_test/24: fail
+mixin_illegal_superclass_test/25: fail
+mixin_illegal_superclass_test/26: fail
+mixin_illegal_superclass_test/27: fail
+mixin_illegal_superclass_test/28: fail
+mixin_illegal_superclass_test/29: fail
+mixin_illegal_superclass_test/30: fail
+mixin_illegal_syntax_test/13: fail
+mixin_type_parameters_errors_test/01: fail
+mixin_type_parameters_errors_test/02: fail
+mixin_type_parameters_errors_test/03: fail
+mixin_type_parameters_errors_test/04: fail
+mixin_type_parameters_errors_test/05: fail
+named_parameters2_test: fail
+named_parameters_aggregated_test/03: fail
+named_parameters_aggregated_test/04: fail
+named_parameters_aggregated_test/05: fail
+named_parameters_type_test: fail
+new_expression_type_args_test/00: fail
+new_expression_type_args_test/01: fail
+new_expression_type_args_test/02: fail
+no_such_method_negative_test: fail
+non_const_super_negative_test: fail
+number_identifier_negative_test: fail
+override_field_method1_negative_test: fail
+override_field_method2_negative_test: fail
+override_field_method4_negative_test: fail
+override_field_method5_negative_test: fail
+override_field_test/01: fail
+override_field_test/02: fail
+override_field_test/03: fail
+override_method_with_field_test/01: fail
+override_method_with_field_test/02: fail
+parameter_initializer3_negative_test: fail
+parameter_initializer5_negative_test: fail
+positional_parameters_type_test: fail
+prefix10_negative_test: fail
+prefix11_negative_test: fail
+prefix12_negative_test: fail
+prefix1_negative_test: fail
+prefix22_test: fail
+prefix23_test: fail
+prefix2_negative_test: fail
+prefix3_negative_test: fail
+prefix4_negative_test: fail
+prefix5_negative_test: fail
+prefix6_negative_test: fail
+prefix8_negative_test: fail
+prefix9_negative_test: fail
+private_member1_negative_test: fail
+private_member2_negative_test: fail
+private_member3_negative_test: fail
+pseudo_kw_illegal_test/14: fail
+pseudo_kw_test: fail
+redirecting_factory_infinite_steps_test/01: fail
+redirecting_factory_infinite_steps_test/02: fail
+scope_negative_test: fail
+setter3_test/01: fail
+setter3_test/02: fail
+setter_declaration2_negative_test: fail
+setter_declaration_negative_test: fail
+static_call_wrong_argument_count_negative_test: fail
+static_field3_test/01: fail
+static_field3_test/02: fail
+static_field3_test/03: fail
+static_field3_test/04: fail
+static_field_test/01: fail
+static_field_test/02: fail
+static_field_test/03: fail
+static_field_test/04: fail
+static_final_field2_negative_test: fail
+static_final_field_negative_test: fail
+string_interpolate1_negative_test: fail
+string_interpolate2_negative_test: fail
+string_interpolation1_negative_test: fail
+string_interpolation2_negative_test: fail
+string_interpolation3_negative_test: fail
+string_interpolation4_negative_test: fail
+string_interpolation5_negative_test: fail
+string_interpolation6_negative_test: fail
+string_interpolation9_test/10: fail
+string_interpolation9_test/11: fail
+string_interpolation9_test/12: fail
+string_interpolation9_test/13: fail
+string_interpolation9_test/14: fail
+string_interpolation9_test/15: fail
+string_interpolation9_test/16: fail
+string_interpolation9_test/17: fail
+string_interpolation9_test/18: fail
+string_interpolation9_test/19: fail
+string_interpolation9_test/1: fail
+string_interpolation9_test/20: fail
+string_interpolation9_test/2: fail
+string_interpolation9_test/3: fail
+string_interpolation9_test/4: fail
+string_interpolation9_test/5: fail
+string_interpolation9_test/6: fail
+string_interpolation9_test/7: fail
+string_interpolation9_test/8: fail
+string_interpolation9_test/9: fail
+string_interpolation_test/01: fail
+super_call3_test/02: fail
+super_operator_index_test/01: fail
+super_operator_index_test/02: fail
+super_operator_index_test/03: fail
+super_operator_index_test/04: fail
+super_operator_index_test/05: fail
+super_operator_index_test/06: fail
+super_operator_index_test/07: fail
+syntax_test/28: fail
+syntax_test/29: fail
+syntax_test/30: fail
+syntax_test/31: fail
+syntax_test/32: fail
+syntax_test/33: fail
+ternary_test: fail
+throw7_negative_test: fail
+try_catch_on_syntax_test/01: fail
+try_catch_on_syntax_test/07: fail
+try_catch_syntax_test/08: fail
+try_catch_syntax_test/16: fail
+try_catch_syntax_test/17: fail
+type_error_test: fail
+type_parameter_test/01: fail
+type_parameter_test/02: fail
+type_parameter_test/03: fail
+type_parameter_test/04: fail
+type_variable_bounds2_test/00: fail
+type_variable_bounds2_test/01: fail
+type_variable_bounds2_test/02: fail
+type_variable_bounds2_test/03: fail
+type_variable_bounds2_test/04: fail
+type_variable_bounds2_test/06: fail
+type_variable_bounds_test/00: fail
+type_variable_bounds_test/01: fail
+type_variable_bounds_test/02: fail
+type_variable_bounds_test/03: fail
+type_variable_bounds_test/04: fail
+type_variable_bounds_test/05: fail
+type_variable_bounds_test/06: fail
+type_variable_bounds_test/07: fail
+type_variable_bounds_test/09: fail
+type_variable_bounds_test/10: fail
+type_variable_identifier_expression_negative_test: fail
+type_variable_static_context_negative_test: fail
+typed_equality_test: fail
+unary2_test: fail
+unary_test: fail
+unresolved_in_factory_negative_test: fail
+unresolved_top_level_method_negative_test: fail
+unresolved_top_level_var_negative_test: fail
+unsigned_right_shift_test/01: fail
+unsigned_right_shift_test/02: fail
+wrong_number_type_arguments_test/00: fail
+wrong_number_type_arguments_test/02: fail
+
+
[ $arch == arm ]
*: Skip
diff --git a/tests/language/load_to_load_forwarding_test.dart b/tests/language/load_to_load_forwarding_test.dart
index 6c7d8b4..cade3c6 100644
--- a/tests/language/load_to_load_forwarding_test.dart
+++ b/tests/language/load_to_load_forwarding_test.dart
@@ -35,10 +35,37 @@
return [value1, value2];
}
+// Verify that immutable and mutable VM fields (array length in this case)
+// are not confused by load forwarding even if the access the same offset
+// in the object.
+testImmutableVMFields(arr, immutable) {
+ if (immutable) {
+ return arr.length; // Immutable length load.
+ }
+
+ if (arr.length < 2) { // Mutable length load, should not be forwarded.
+ arr.add(null);
+ }
+
+ return arr.length;
+}
+
main() {
+ final fixed = new List(10);
+ final growable = [];
+ testImmutableVMFields(fixed, true);
+ testImmutableVMFields(growable, false);
+ testImmutableVMFields(growable, false);
+
for (var i = 0; i < 2000; i++) {
Expect.listEquals([0x02010000, 0x03020100], foo(new A(0, 0)));
Expect.listEquals([0x02010000, 0x03020100], bar(new A(0, 0), false));
Expect.listEquals([0x04020000, 0x03020100], bar(new A(0, 0), true));
+ testImmutableVMFields(fixed, true);
}
+
+ Expect.equals(1, testImmutableVMFields([], false));
+ Expect.equals(2, testImmutableVMFields([1], false));
+ Expect.equals(2, testImmutableVMFields([1, 2], false));
+ Expect.equals(3, testImmutableVMFields([1, 2, 3], false));
}
\ No newline at end of file
diff --git a/tests/lib/analyzer/analyze_tests.status b/tests/lib/analyzer/analyze_tests.status
index d949e2b..949fd4f 100644
--- a/tests/lib/analyzer/analyze_tests.status
+++ b/tests/lib/analyzer/analyze_tests.status
@@ -35,3 +35,6 @@
standalone/io/http_parser_test: Fail
standalone/io/web_socket_protocol_processor_test: Fail
standalone/io/url_encoding_test: Fail
+
+[ $compiler == dartanalyzer ]
+standalone/io/process_exit_negative_test: fail
diff --git a/tests/lib/async/future_test.dart b/tests/lib/async/future_test.dart
index 078ee4a..7af6893 100644
--- a/tests/lib/async/future_test.dart
+++ b/tests/lib/async/future_test.dart
@@ -76,15 +76,17 @@
final completer = new Completer<int>();
final future = completer.future;
- int value;
- future.then((int v) { value = v; });
- Expect.isNull(value);
+ int after;
- Expect.isFalse(completer.isCompleted);
+ var port = new ReceivePort();
+ future.then((int v) { after = v; })
+ .then((_) {
+ Expect.equals(3, after);
+ port.close();
+ });
+
completer.complete(3);
- Expect.isTrue(completer.isCompleted);
-
- Expect.equals(3, value);
+ Expect.isNull(after);
}
testCompleteWithSuccessHandlerAfterComplete() {
diff --git a/tests/standalone/io/http_auth_digest_test.dart b/tests/standalone/io/http_auth_digest_test.dart
index 70e1451..74e9d93 100644
--- a/tests/standalone/io/http_auth_digest_test.dart
+++ b/tests/standalone/io/http_auth_digest_test.dart
@@ -67,7 +67,7 @@
HeaderValue header =
HeaderValue.parse(
authorization, parameterSeparator: ",");
- if (header.value == "basic") {
+ if (header.value.toLowerCase() == "basic") {
sendUnauthorizedResponse(response);
} else if (!useNextNonce && nonceCount == nonceStaleAfter) {
nonce = "87654321";
@@ -78,12 +78,12 @@
var qop = header.parameters["qop"];
var cnonce = header.parameters["cnonce"];
var nc = header.parameters["nc"];
- Expect.equals("digest", header.value);
+ Expect.equals("digest", header.value.toLowerCase());
Expect.equals("dart", header.parameters["username"]);
Expect.equals(realm, header.parameters["realm"]);
Expect.equals("MD5", header.parameters["algorithm"]);
Expect.equals(nonce, header.parameters["nonce"]);
- Expect.equals(request.uri.path, uri);
+ Expect.equals(request.uri.toString(), uri);
if (qop != null) {
// A server qop of auth-int is downgraded to none by the client.
Expect.equals("auth", serverQop);
@@ -201,10 +201,17 @@
new HttpClientDigestCredentials("dart", "password"));
var futures = [];
- for (int i = 0; i < 5; i++) {
+ for (int i = 0; i < 2; i++) {
+ String uriBase = "http://127.0.0.1:${server.port}/digest";
futures.add(
makeRequest(
- Uri.parse("http://127.0.0.1:${server.port}/digest")));
+ Uri.parse(uriBase)));
+ futures.add(
+ makeRequest(
+ Uri.parse("$uriBase?querystring")));
+ futures.add(
+ makeRequest(
+ Uri.parse("$uriBase?querystring#fragment")));
}
Future.wait(futures).then((_) {
server.shutdown();
diff --git a/tests/standalone/io/http_body_test.dart b/tests/standalone/io/http_body_test.dart
index cd1c4b5..a6ea742 100644
--- a/tests/standalone/io/http_body_test.dart
+++ b/tests/standalone/io/http_body_test.dart
@@ -90,7 +90,7 @@
List<int> content,
dynamic expectedBody,
String type,
- [bool shouldFail = false]) {
+ {bool shouldFail: false}) {
HttpServer.bind("127.0.0.1", 0).then((server) {
server.transform(new HttpBodyHandler())
.listen((body) {
@@ -98,26 +98,52 @@
Expect.equals(type, body.type);
switch (type) {
case "text":
- Expect.equals(body.mimeType, "text/plain");
+ Expect.equals(body.contentType.mimeType, "text/plain");
Expect.equals(expectedBody, body.body);
break;
case "json":
- Expect.equals(body.mimeType, "application/json");
+ Expect.equals(body.contentType.mimeType, "application/json");
Expect.mapEquals(expectedBody, body.body);
break;
case "binary":
- Expect.equals(body.mimeType, null);
+ Expect.equals(body.contentType, null);
Expect.listEquals(expectedBody, body.body);
break;
+ case "form":
+ var mimeType = body.contentType.mimeType;
+ Expect.isTrue(
+ mimeType == 'multipart/form-data' ||
+ mimeType == 'application/x-www-form-urlencoded');
+ Expect.setEquals(expectedBody.keys.toSet(),
+ body.body.keys.toSet());
+ for (var key in expectedBody.keys) {
+ if (body.body[key] is HttpBodyFileUpload) {
+ Expect.equals(expectedBody[key]['contentType'],
+ body.body[key].contentType.toString());
+ Expect.equals(expectedBody[key]['filename'],
+ body.body[key].filename);
+ if (body.body[key].content is String) {
+ Expect.equals(expectedBody[key]['content'],
+ body.body[key].content);
+ } else {
+ Expect.listEquals(expectedBody[key]['content'],
+ body.body[key].content);
+ }
+ } else {
+ Expect.equals(expectedBody[key], body.body[key]);
+ }
+ }
+ break;
+
default:
Expect.fail("bad body type");
}
body.response.close();
}, onError: (error) {
- if (!shouldFail) Expect.fail("Error unexpected");
+ if (!shouldFail) throw error;
});
var client = new HttpClient();
@@ -160,9 +186,70 @@
'{ bad json }'.codeUnits,
null,
"json",
- true);
+ shouldFail: true);
test(null, "body".codeUnits, "body".codeUnits, "binary");
+
+ test("multipart/form-data; boundary=AaB03x",
+ '''
+--AaB03x\r
+Content-Disposition: form-data; name="name"\r
+\r
+Larry\r
+--AaB03x--\r\n'''.codeUnits,
+ { "name": "Larry" },
+ "form");
+
+ test("multipart/form-data; boundary=AaB03x",
+ '''
+--AaB03x\r
+Content-Disposition: form-data; name="files"; filename="myfile"\r
+Content-Type: application/octet-stream\r
+\r
+File content\r
+--AaB03x--\r\n'''.codeUnits,
+ { "files": { 'filename': 'myfile',
+ 'contentType': 'application/octet-stream',
+ 'content': 'File content'.codeUnits} },
+ "form");
+
+ test("multipart/form-data; boundary=AaB03x",
+ '''
+--AaB03x\r
+Content-Disposition: form-data; name="files"; filename="myfile"\r
+Content-Type: application/octet-stream\r
+\r
+File content\r
+--AaB03x\r
+Content-Disposition: form-data; name="files"; filename="myfile"\r
+Content-Type: text/plain\r
+\r
+File content\r
+--AaB03x--\r\n'''.codeUnits,
+ { "files": { 'filename': 'myfile',
+ 'contentType': 'text/plain',
+ 'content': 'File content'} },
+ "form");
+
+ test("multipart/form-data; boundary=AaB03x",
+ '''
+--AaB03x\r
+Content-Disposition: form-data; name="files"; filename="myfile"\r
+Content-Type: application/json\r
+\r
+File content\r
+--AaB03x--\r\n'''.codeUnits,
+ { "files": { 'filename': 'myfile',
+ 'contentType': 'application/json',
+ 'content': 'File content'} },
+ "form");
+
+ test('application/x-www-form-urlencoded',
+ '%E5%B9%B3%3D%E4%BB%AE%E5%90%8D=%26%2324179%3B%26%2320206%3B%26%'
+ '2321517%3B&b=%26%2324179%3B%26%2320206%3B%26%2321517%3B'.codeUnits,
+ { 'b' : '平仮名',
+ '平=仮名' : '平仮名'},
+ "form");
}
void main() {
diff --git a/tests/standalone/io/http_multipart_test.dart b/tests/standalone/io/http_multipart_test.dart
new file mode 100644
index 0000000..d564fc9
--- /dev/null
+++ b/tests/standalone/io/http_multipart_test.dart
@@ -0,0 +1,201 @@
+// 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.
+//
+// VMOptions=
+// VMOptions=--short_socket_read
+// VMOptions=--short_socket_write
+// VMOptions=--short_socket_read --short_socket_write
+
+import "package:expect/expect.dart";
+import 'dart:async';
+import 'dart:io';
+
+class FormField {
+ final String name;
+ final value;
+ final String contentType;
+ final String filename;
+
+ FormField(String this.name,
+ this.value,
+ {String this.contentType,
+ String this.filename});
+
+ bool operator==(other) {
+ if (value.length != other.value.length) return false;
+ for (int i = 0; i < value.length; i++) {
+ if (value[i] != other.value[i]) {
+ return false;
+ }
+ }
+ return name == other.name &&
+ contentType == other.contentType &&
+ filename == other.filename;
+ }
+
+ String toString() {
+ return "FormField('$name', '$value', '$contentType', '$filename')";
+ }
+}
+
+void postDataTest(List<int> message,
+ String contentType,
+ String boundary,
+ List<FormField> expectedFields) {
+ HttpServer.bind("127.0.0.1", 0).then((server) {
+ server.listen((request) {
+ String boundary = request.headers.contentType.parameters['boundary'];
+ request
+ .transform(new MimeMultipartTransformer(boundary))
+ .map(HttpMultipartFormData.parse)
+ .map((multipart) {
+ var future;
+ if (multipart.isText) {
+ future = multipart
+ .fold(new StringBuffer(), (b, s) => b..write(s))
+ .then((b) => b.toString());
+ } else {
+ future = multipart
+ .fold([], (b, s) => b..addAll(s));
+ }
+ return future
+ .then((data) {
+ String contentType;
+ if (multipart.contentType != null) {
+ contentType = multipart.contentType.mimeType;
+ }
+ return new FormField(
+ multipart.contentDisposition.parameters['name'],
+ data,
+ contentType: contentType,
+ filename:
+ multipart.contentDisposition.parameters['filename']);
+ });
+ })
+ .fold([], (l, f) => l..add(f))
+ .then(Future.wait)
+ .then((fields) {
+ Expect.listEquals(expectedFields, fields);
+ request.response.close().then((_) => server.close());
+ });
+ });
+ var client = new HttpClient();
+ client.post('127.0.0.1', server.port, '/')
+ .then((request) {
+ request.headers.set('content-type',
+ 'multipart/form-data; boundary=$boundary');
+ request.add(message);
+ return request.close();
+ })
+ .then((response) {
+ client.close();
+ });
+ });
+}
+
+void testPostData() {
+ var message = '''
+\r\n--AaB03x\r
+Content-Disposition: form-data; name="submit-name"\r
+\r
+Larry\r
+--AaB03x\r
+Content-Disposition: form-data; name="files"; filename="file1.txt"\r
+Content-Type: text/plain\r
+\r
+Content of file\r
+--AaB03x--\r\n''';
+
+
+ postDataTest(message.codeUnits,
+ 'multipart/form-data',
+ 'AaB03x',
+ [new FormField('submit-name', 'Larry'),
+ new FormField('files',
+ 'Content of file',
+ contentType: 'text/plain',
+ filename: 'file1.txt')]);
+
+ // Similar test using Chrome posting.
+ message = [
+ 45, 45, 45, 45, 45, 45, 87, 101, 98, 75, 105, 116, 70, 111, 114, 109, 66,
+ 111, 117, 110, 100, 97, 114, 121, 81, 83, 113, 108, 56, 107, 68, 65, 76,
+ 77, 55, 116, 65, 107, 67, 49, 13, 10, 67, 111, 110, 116, 101, 110, 116,
+ 45, 68, 105, 115, 112, 111, 115, 105, 116, 105, 111, 110, 58, 32, 102,
+ 111, 114, 109, 45, 100, 97, 116, 97, 59, 32, 110, 97, 109, 101, 61, 34,
+ 115, 117, 98, 109, 105, 116, 45, 110, 97, 109, 101, 34, 13, 10, 13, 10,
+ 84, 101, 115, 116, 13, 10, 45, 45, 45, 45, 45, 45, 87, 101, 98, 75, 105,
+ 116, 70, 111, 114, 109, 66, 111, 117, 110, 100, 97, 114, 121, 81, 83, 113,
+ 108, 56, 107, 68, 65, 76, 77, 55, 116, 65, 107, 67, 49, 13, 10, 67, 111,
+ 110, 116, 101, 110, 116, 45, 68, 105, 115, 112, 111, 115, 105, 116, 105,
+ 111, 110, 58, 32, 102, 111, 114, 109, 45, 100, 97, 116, 97, 59, 32, 110,
+ 97, 109, 101, 61, 34, 102, 105, 108, 101, 115, 34, 59, 32, 102, 105, 108,
+ 101, 110, 97, 109, 101, 61, 34, 86, 69, 82, 83, 73, 79, 78, 34, 13, 10,
+ 67, 111, 110, 116, 101, 110, 116, 45, 84, 121, 112, 101, 58, 32, 97, 112,
+ 112, 108, 105, 99, 97, 116, 105, 111, 110, 47, 111, 99, 116, 101, 116, 45,
+ 115, 116, 114, 101, 97, 109, 13, 10, 13, 10, 123, 32, 10, 32, 32, 34, 114,
+ 101, 118, 105, 115, 105, 111, 110, 34, 58, 32, 34, 50, 49, 56, 54, 48, 34,
+ 44, 10, 32, 32, 34, 118, 101, 114, 115, 105, 111, 110, 34, 32, 58, 32, 34,
+ 48, 46, 49, 46, 50, 46, 48, 95, 114, 50, 49, 56, 54, 48, 34, 44, 10, 32,
+ 32, 34, 100, 97, 116, 101, 34, 32, 32, 32, 32, 58, 32, 34, 50, 48, 49, 51,
+ 48, 52, 50, 51, 48, 48, 48, 52, 34, 10, 125, 13, 10, 45, 45, 45, 45, 45,
+ 45, 87, 101, 98, 75, 105, 116, 70, 111, 114, 109, 66, 111, 117, 110, 100,
+ 97, 114, 121, 81, 83, 113, 108, 56, 107, 68, 65, 76, 77, 55, 116, 65, 107,
+ 67, 49, 45, 45, 13, 10];
+
+ var data = [
+ 123, 32, 10, 32, 32, 34, 114, 101, 118, 105, 115, 105, 111, 110, 34, 58,
+ 32, 34, 50, 49, 56, 54, 48, 34, 44, 10, 32, 32, 34, 118, 101, 114, 115,
+ 105, 111, 110, 34, 32, 58, 32, 34, 48, 46, 49, 46, 50, 46, 48, 95, 114,
+ 50, 49, 56, 54, 48, 34, 44, 10, 32, 32, 34, 100, 97, 116, 101, 34, 32, 32,
+ 32, 32, 58, 32, 34, 50, 48, 49, 51, 48, 52, 50, 51, 48, 48, 48, 52, 34,
+ 10, 125];
+
+ postDataTest(message,
+ 'multipart/form-data',
+ '----WebKitFormBoundaryQSql8kDALM7tAkC1',
+ [new FormField('submit-name', 'Test'),
+ new FormField('files',
+ data,
+ contentType: 'application/octet-stream',
+ filename: 'VERSION')]);
+
+ message = [
+ 45, 45, 45, 45, 45, 45, 87, 101, 98, 75, 105, 116, 70, 111, 114, 109, 66,
+ 111, 117, 110, 100, 97, 114, 121, 118, 65, 86, 122, 117, 103, 75, 77, 116,
+ 90, 98, 121, 87, 111, 66, 71, 13, 10, 67, 111, 110, 116, 101, 110, 116,
+ 45, 68, 105, 115, 112, 111, 115, 105, 116, 105, 111, 110, 58, 32, 102,
+ 111, 114, 109, 45, 100, 97, 116, 97, 59, 32, 110, 97, 109, 101, 61, 34,
+ 110, 97, 109, 101, 34, 13, 10, 13, 10, 38, 35, 49, 50, 52, 48, 50, 59, 38,
+ 35, 49, 50, 52, 50, 53, 59, 38, 35, 49, 50, 51, 54, 52, 59, 38, 35, 49,
+ 50, 51, 57, 52, 59, 13, 10, 45, 45, 45, 45, 45, 45, 87, 101, 98, 75, 105,
+ 116, 70, 111, 114, 109, 66, 111, 117, 110, 100, 97, 114, 121, 118, 65, 86,
+ 122, 117, 103, 75, 77, 116, 90, 98, 121, 87, 111, 66, 71, 45, 45, 13, 10];
+
+ postDataTest(message,
+ 'multipart/form-data',
+ '----WebKitFormBoundaryvAVzugKMtZbyWoBG',
+ [new FormField('name', 'ひらがな')]);
+
+ message = [
+ 45, 45, 45, 45, 45, 45, 87, 101, 98, 75, 105, 116, 70, 111, 114, 109, 66,
+ 111, 117, 110, 100, 97, 114, 121, 102, 101, 48, 69, 122, 86, 49, 97, 78,
+ 121, 115, 68, 49, 98, 80, 104, 13, 10, 67, 111, 110, 116, 101, 110, 116,
+ 45, 68, 105, 115, 112, 111, 115, 105, 116, 105, 111, 110, 58, 32, 102,
+ 111, 114, 109, 45, 100, 97, 116, 97, 59, 32, 110, 97, 109, 101, 61, 34,
+ 110, 97, 109, 101, 34, 13, 10, 13, 10, 248, 118, 13, 10, 45, 45, 45, 45,
+ 45, 45, 87, 101, 98, 75, 105, 116, 70, 111, 114, 109, 66, 111, 117, 110,
+ 100, 97, 114, 121, 102, 101, 48, 69, 122, 86, 49, 97, 78, 121, 115, 68,
+ 49, 98, 80, 104, 45, 45, 13, 10];
+
+ postDataTest(message,
+ 'multipart/form-data',
+ '----WebKitFormBoundaryfe0EzV1aNysD1bPh',
+ [new FormField('name', 'øv')]);
+}
+
+
+void main() {
+ testPostData();
+}
diff --git a/tests/standalone/io/http_parser_test.dart b/tests/standalone/io/http_parser_test.dart
index 5742510..0030a7d 100644
--- a/tests/standalone/io/http_parser_test.dart
+++ b/tests/standalone/io/http_parser_test.dart
@@ -275,13 +275,13 @@
}
static void _testParseInvalidResponse(String response, [bool close = false]) {
- _HttpParser httpParser;
- bool errorCalled;
- StreamController controller;
+ void testWrite(List<int> requestData, [int chunkSize = -1]) {
+ _HttpParser httpParser = new _HttpParser.responseParser();
+ StreamController controller = new StreamController();
+ bool errorCalled = false;;
- void reset() {
- httpParser = new _HttpParser.responseParser();
- controller = new StreamController();
+ if (chunkSize == -1) chunkSize = requestData.length;
+
var port = new ReceivePort();
controller.stream.pipe(httpParser);
var subscription = httpParser.listen((incoming) {
@@ -300,12 +300,8 @@
port.close();
Expect.isTrue(errorCalled);
});
- errorCalled = false;
- }
- void testWrite(List<int> requestData, [int chunkSize = -1]) {
- if (chunkSize == -1) chunkSize = requestData.length;
- reset();
+ errorCalled = false;
for (int pos = 0;
pos < requestData.length && !errorCalled;
pos += chunkSize) {
diff --git a/tests/standalone/io/http_proxy_test.dart b/tests/standalone/io/http_proxy_test.dart
index 197b11a..abab0bb 100644
--- a/tests/standalone/io/http_proxy_test.dart
+++ b/tests/standalone/io/http_proxy_test.dart
@@ -80,21 +80,65 @@
HttpServer server;
HttpClient client;
int requestCount = 0;
+ String authScheme;
+ String realm = "test";
String username;
String password;
+ var ha1;
+ String serverAlgorithm = "MD5";
+ String serverQop = "auth";
+ Set ncs = new Set();
+
+ var nonce = "12345678"; // No need for random nonce in test.
+
ProxyServer() : client = new HttpClient();
- authenticationRequired(request) {
+ void useBasicAuthentication(String username, String password) {
+ this.username = username;
+ this.password = password;
+ authScheme = "Basic";
+ }
+
+ void useDigestAuthentication(String username, String password) {
+ this.username = username;
+ this.password = password;
+ authScheme = "Digest";
+
+ // Calculate ha1.
+ var hasher = new MD5();
+ hasher.add("${username}:${realm}:${password}".codeUnits);
+ ha1 = CryptoUtils.bytesToHex(hasher.close());
+ }
+
+ basicAuthenticationRequired(request) {
request.fold(null, (x, y) {}).then((_) {
var response = request.response;
response.headers.set(HttpHeaders.PROXY_AUTHENTICATE,
- "Basic, realm=realm");
+ "Basic, realm=$realm");
response.statusCode = HttpStatus.PROXY_AUTHENTICATION_REQUIRED;
response.close();
});
}
+ digestAuthenticationRequired(request, {stale: false}) {
+ request.fold(null, (x, y) {}).then((_) {
+ var response = request.response;
+ response.statusCode = HttpStatus.PROXY_AUTHENTICATION_REQUIRED;
+ StringBuffer authHeader = new StringBuffer();
+ authHeader.write('Digest');
+ authHeader.write(', realm="$realm"');
+ authHeader.write(', nonce="$nonce"');
+ if (stale) authHeader.write(', stale="true"');
+ if (serverAlgorithm != null) {
+ authHeader.write(', algorithm=$serverAlgorithm');
+ }
+ if (serverQop != null) authHeader.write(', qop="$serverQop"');
+ response.headers.set(HttpHeaders.PROXY_AUTHENTICATE, authHeader);
+ response.close();
+ });
+ }
+
Future<ProxyServer> start() {
var x = new Completer();
HttpServer.bind("localhost", 0).then((s) {
@@ -104,20 +148,75 @@
requestCount++;
if (username != null && password != null) {
if (request.headers[HttpHeaders.PROXY_AUTHORIZATION] == null) {
- authenticationRequired(request);
+ if (authScheme == "Digest") {
+ digestAuthenticationRequired(request);
+ } else {
+ basicAuthenticationRequired(request);
+ }
return;
} else {
Expect.equals(
1, request.headers[HttpHeaders.PROXY_AUTHORIZATION].length);
String authorization =
request.headers[HttpHeaders.PROXY_AUTHORIZATION][0];
- List<String> tokens = authorization.split(" ");
- Expect.equals("Basic", tokens[0]);
- String auth =
- CryptoUtils.bytesToBase64(encodeUtf8("$username:$password"));
- if (auth != tokens[1]) {
- authenticationRequired(request);
- return;
+ if (authScheme == "Basic") {
+ List<String> tokens = authorization.split(" ");
+ Expect.equals("Basic", tokens[0]);
+ String auth =
+ CryptoUtils.bytesToBase64(encodeUtf8("$username:$password"));
+ if (auth != tokens[1]) {
+ basicAuthenticationRequired(request);
+ return;
+ }
+ } else {
+ HeaderValue header =
+ HeaderValue.parse(
+ authorization, parameterSeparator: ",");
+ Expect.equals("Digest", header.value);
+ var uri = header.parameters["uri"];
+ var qop = header.parameters["qop"];
+ var cnonce = header.parameters["cnonce"];
+ var nc = header.parameters["nc"];
+ Expect.equals(username, header.parameters["username"]);
+ Expect.equals(realm, header.parameters["realm"]);
+ Expect.equals("MD5", header.parameters["algorithm"]);
+ Expect.equals(nonce, header.parameters["nonce"]);
+ Expect.equals(request.uri.toString(), uri);
+ if (qop != null) {
+ // A server qop of auth-int is downgraded to none by the client.
+ Expect.equals("auth", serverQop);
+ Expect.equals("auth", header.parameters["qop"]);
+ Expect.isNotNull(cnonce);
+ Expect.isNotNull(nc);
+ Expect.isFalse(ncs.contains(nc));
+ ncs.add(nc);
+ } else {
+ Expect.isNull(cnonce);
+ Expect.isNull(nc);
+ }
+ Expect.isNotNull(header.parameters["response"]);
+
+ var hasher = new MD5();
+ hasher.add("${request.method}:${uri}".codeUnits);
+ var ha2 = CryptoUtils.bytesToHex(hasher.close());
+
+ var x;
+ hasher = new MD5();
+ if (qop == null || qop == "" || qop == "none") {
+ hasher.add("$ha1:${nonce}:$ha2".codeUnits);
+ } else {
+ hasher.add(
+ "$ha1:${nonce}:${nc}:${cnonce}:${qop}:$ha2".codeUnits);
+ }
+ Expect.equals(CryptoUtils.bytesToHex(hasher.close()),
+ header.parameters["response"]);
+
+ // Add a bogus Proxy-Authentication-Info for testing.
+ var info = 'rspauth="77180d1ab3d6c9de084766977790f482", '
+ 'cnonce="8f971178", '
+ 'nc=000002c74, '
+ 'qop=auth';
+ request.response.headers.set("Proxy-Authentication-Info", info);
}
}
}
@@ -412,10 +511,11 @@
int testProxyAuthenticateCount = 0;
-void testProxyAuthenticate() {
+Future testProxyAuthenticate(bool useDigestAuthentication) {
+ testProxyAuthenticateCount = 0;
+ var completer = new Completer();
+
setupProxyServer().then((proxyServer) {
- proxyServer.username = "test";
- proxyServer.password = "test";
setupServer(1).then((server) {
setupServer(1, secure: true).then((secureServer) {
HttpClient client = new HttpClient();
@@ -423,6 +523,12 @@
Completer step1 = new Completer();
Completer step2 = new Completer();
+ if (useDigestAuthentication) {
+ proxyServer.useDigestAuthentication("dart", "password");
+ } else {
+ proxyServer.useBasicAuthentication("dart", "password");
+ }
+
// Test with no authentication.
client.findProxy = (Uri uri) {
return "PROXY localhost:${proxyServer.port}";
@@ -459,15 +565,25 @@
}
step1.future.then((_) {
testProxyAuthenticateCount = 0;
- client.findProxy = (Uri uri) {
- return "PROXY test:test@localhost:${proxyServer.port}";
- };
+ if (useDigestAuthentication) {
+ client.findProxy = (Uri uri) => "PROXY localhost:${proxyServer.port}";
+ client.addProxyCredentials(
+ "localhost",
+ proxyServer.port,
+ "test",
+ new HttpClientDigestCredentials("dart", "password"));
+ } else {
+ client.findProxy = (Uri uri) {
+ return "PROXY dart:password@localhost:${proxyServer.port}";
+ };
+ }
for (int i = 0; i < loopCount; i++) {
test(bool secure) {
+ var path = useDigestAuthentication ? "A" : "$i";
String url = secure
- ? "https://localhost:${secureServer.port}/$i"
- : "http://localhost:${server.port}/$i";
+ ? "https://localhost:${secureServer.port}/$path"
+ : "http://localhost:${server.port}/$path";
client.postUrl(Uri.parse(url))
.then((HttpClientRequest clientRequest) {
@@ -504,7 +620,7 @@
"localhost",
proxyServer.port,
"realm",
- new HttpClientBasicCredentials("test", "test"));
+ new HttpClientBasicCredentials("dart", "password"));
return new Future.value(true);
};
@@ -531,6 +647,7 @@
server.shutdown();
secureServer.shutdown();
client.close();
+ completer.complete(null);
}
});
});
@@ -543,16 +660,19 @@
});
});
});
+
+ return completer.future;
}
int testRealProxyDoneCount = 0;
void testRealProxy() {
setupServer(1).then((server) {
HttpClient client = new HttpClient();
- client.addProxyCredentials("localhost",
- 8080,
- "test",
- new HttpClientBasicCredentials("test", "test"));
+ client.addProxyCredentials(
+ "localhost",
+ 8080,
+ "test",
+ new HttpClientBasicCredentials("dart", "password"));
List<String> proxy =
["PROXY localhost:8080",
@@ -593,10 +713,10 @@
HttpClient client = new HttpClient();
List<String> proxy =
- ["PROXY test:test@localhost:8080",
- "PROXY test:test@localhost:8080; PROXY hede.hule.hest:8080",
- "PROXY hede.hule.hest:8080; PROXY test:test@localhost:8080",
- "PROXY test:test@localhost:8080; DIRECT"];
+ ["PROXY dart:password@localhost:8080",
+ "PROXY dart:password@localhost:8080; PROXY hede.hule.hest:8080",
+ "PROXY hede.hule.hest:8080; PROXY dart:password@localhost:8080",
+ "PROXY dart:password@localhost:8080; DIRECT"];
client.findProxy = (Uri uri) {
// Pick the proxy configuration based on the request path.
@@ -639,7 +759,10 @@
testProxy();
testProxyChain();
testProxyFromEnviroment();
- testProxyAuthenticate();
+ // The two invocations of uses the same global variable for state -
+ // run one after the other.
+ testProxyAuthenticate(false)
+ .then((_) => testProxyAuthenticate(true));
// This test is not normally run. It can be used for locally testing
// with a real proxy server (e.g. Apache).
//testRealProxy();
diff --git a/tests/standalone/io/http_server_early_client_close2_test.dart b/tests/standalone/io/http_server_early_client_close2_test.dart
new file mode 100644
index 0000000..bc8b6f6
--- /dev/null
+++ b/tests/standalone/io/http_server_early_client_close2_test.dart
@@ -0,0 +1,37 @@
+// Copyright (c) 2013, 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.
+
+import "package:expect/expect.dart";
+import "dart:async";
+import "dart:io";
+import "dart:isolate";
+
+main() {
+ HttpServer.bind("127.0.0.1", 0).then((server) {
+ server.listen(
+ (request) {
+ String name = new Options().script;
+ new File(name).openRead().pipe(request.response)
+ .catchError((e) { /* ignore */ });
+ });
+
+ var count = 0;
+ makeRequest() {
+ Socket.connect("127.0.0.1", server.port).then((socket) {
+ var data = "GET / HTTP/1.1\r\nContent-Length: 0\r\n\r\n";
+ socket.write(data);
+ socket.close();
+ socket.done.then((_) {
+ socket.destroy();
+ if (++count < 10) {
+ makeRequest();
+ } else {
+ server.close();
+ }
+ });
+ });
+ }
+ makeRequest();
+ });
+}
diff --git a/tests/standalone/io/http_server_early_client_close_test.dart b/tests/standalone/io/http_server_early_client_close_test.dart
index c4ec244..d35aaf4 100644
--- a/tests/standalone/io/http_server_early_client_close_test.dart
+++ b/tests/standalone/io/http_server_early_client_close_test.dart
@@ -125,7 +125,7 @@
if (++count < 10) {
makeRequest();
} else {
- server.close();
+ runAsync(server.close);
}
});
});
diff --git a/tests/standalone/io/mime_multipart_parser_test.dart b/tests/standalone/io/mime_multipart_parser_test.dart
index 1045b5a..5a81df7 100644
--- a/tests/standalone/io/mime_multipart_parser_test.dart
+++ b/tests/standalone/io/mime_multipart_parser_test.dart
@@ -279,12 +279,8 @@
--\r\r\r
-\r""";
testParse(message, "boundary", [headers, headers], [body1, body2]);
-}
-void testParseInvalid() {
- String message;
-
- // Missing initial CRLF. One body part less.
+ // Without initial CRLF.
message = """
--xxx\r
\r
@@ -295,7 +291,11 @@
\r
Body2\r
--xxx--\r\n""";
- testParse(message, "xxx", null, ["\r\nBody2"]);
+ testParse(message, "xxx", null, ["\r\nBody 1", "\r\nBody2"]);
+}
+
+void testParseInvalid() {
+ String message;
// Missing end boundary.
message = """
diff --git a/tests/standalone/standalone.status b/tests/standalone/standalone.status
index 98e86d9..78a9864 100644
--- a/tests/standalone/standalone.status
+++ b/tests/standalone/standalone.status
@@ -17,6 +17,8 @@
[ $runtime == vm ]
package/package_isolate_test: Fail # http://dartbug.com/7520.
io/raw_server_socket_cancel_test: Pass, Fail, Timeout # Issue 8675
+io/http_server_early_client_close_test: Pass, Fail # http://dartbug.com/10408
+io/http_server_early_client_close2_test: Pass, Fail # http://dartbug.com/10408
[ $runtime == vm && $checked ]
# These tests have type errors on purpose.
@@ -36,6 +38,8 @@
# of allowed open files ('ulimit -n' says something like 256).
io/socket_many_connections_test: Skip
+debugger/basic_debugger_test: Pass, Crash # Issue 10488
+
# These tests pass on MacOS 10.8.2 but fails on the buildbot machines
# that are running an earlier version of MacOS. The issue is that the
# old version of MacOS does not expand the precomposed utf-8 it gets
@@ -74,6 +78,17 @@
io/process_exit_negative_test: Fail # This is a compilation-time negative test.
+[ $compiler == dartanalyzer ]
+crypto/base64_test: fail
+io/file_constructor_test: fail
+io/http_date_test: fail
+io/http_headers_test: fail
+io/http_parser_test: fail
+io/process_exit_negative_test: fail
+io/url_encoding_test: fail
+io/web_socket_protocol_processor_test: fail
+
+
[ $compiler == dart2js ]
number_identity_test: Skip # Bigints and int/double diff. not supported.
typed_data_test: Skip # dart:typed_data support needed.
diff --git a/tests/utils/dummy_compiler_test.dart b/tests/utils/dummy_compiler_test.dart
index 470d79a..3ed3e17 100644
--- a/tests/utils/dummy_compiler_test.dart
+++ b/tests/utils/dummy_compiler_test.dart
@@ -11,7 +11,6 @@
import '../../sdk/lib/_internal/compiler/compiler.dart';
Future<String> provider(Uri uri) {
- Completer<String> completer = new Completer<String>();
String source;
if (uri.scheme == "main") {
source = "main() {}";
@@ -80,8 +79,7 @@
} else {
throw "unexpected URI $uri";
}
- completer.complete(source);
- return completer.future;
+ return new Future.value(source);
}
void handler(Uri uri, int begin, int end, String message, Diagnostic kind) {
diff --git a/tests/utils/recursive_import_test.dart b/tests/utils/recursive_import_test.dart
index 15a09be..d411707 100644
--- a/tests/utils/recursive_import_test.dart
+++ b/tests/utils/recursive_import_test.dart
@@ -61,7 +61,6 @@
main() {
int count = 0;
Future<String> provider(Uri uri) {
- Completer<String> completer = new Completer<String>();
String source;
if (uri.path.length > 100) {
// Simulate an OS error.
@@ -84,8 +83,7 @@
} else {
throw "unexpected URI $uri";
}
- completer.complete(source);
- return completer.future;
+ return new Future.value(source);
}
int warningCount = 0;
diff --git a/tools/VERSION b/tools/VERSION
index 3671ff7..49be911 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -1,4 +1,4 @@
MAJOR 0
MINOR 5
-BUILD 5
+BUILD 6
PATCH 0
diff --git a/tools/dom/scripts/dartmetadata.py b/tools/dom/scripts/dartmetadata.py
index ddb441c..3a10a55 100644
--- a/tools/dom/scripts/dartmetadata.py
+++ b/tools/dom/scripts/dartmetadata.py
@@ -14,7 +14,7 @@
import os
import re
from generator import ConstantOutputOrder
-from htmlrenamer import renamed_html_members
+from htmlrenamer import renamed_html_members, html_interface_renames
_logger = logging.getLogger('DartMetadata')
@@ -445,6 +445,10 @@
if key in _annotations:
annotations.extend(_annotations[key])
+ if (not member_name and interface_name.startswith('WebKit') and
+ interface_name not in html_interface_renames):
+ annotations.extend(_webkit_experimental_annotations)
+
if (member_name and member_name.startswith('webkit') and
key not in renamed_html_members):
annotations.extend(_webkit_experimental_annotations)
diff --git a/tools/dom/scripts/htmlrenamer.py b/tools/dom/scripts/htmlrenamer.py
index 8ab7081..9073f98 100644
--- a/tools/dom/scripts/htmlrenamer.py
+++ b/tools/dom/scripts/htmlrenamer.py
@@ -28,12 +28,8 @@
'Database': 'SqlDatabase', # Avoid conflict with Index DB's Database.
'DatabaseSync': 'SqlDatabaseSync',
'DOMApplicationCache': 'ApplicationCache',
- 'DOMCoreException': 'DomException',
'DOMFileSystem': 'FileSystem',
- 'DOMFormData': 'FormData',
'DOMPoint': '_DomPoint',
- 'DOMURL': 'Url',
- 'DOMWindow': 'Window',
'EntryCallback': '_EntryCallback',
'EntriesCallback': '_EntriesCallback',
'ErrorCallback': '_ErrorCallback',
@@ -59,9 +55,6 @@
'SVGGradientElement': '_GradientElement',
'SVGSVGElement': 'SvgSvgElement', # Manual to avoid name conflicts.
'WebGLVertexArrayObjectOES': 'VertexArrayObject',
- 'WebKitCSSKeyframeRule': 'CssKeyframeRule',
- 'WebKitCSSKeyframesRule': 'CssKeyframesRule',
- 'WebKitCSSTransformValue': 'CssTransformValue',
'XMLHttpRequest': 'HttpRequest',
'XMLHttpRequestException': 'HttpRequestException',
'XMLHttpRequestProgressEvent': 'HttpRequestProgressEvent',
@@ -649,7 +642,7 @@
if any(interface.id in ['Element', 'Document']
for interface in self._database.Hierarchy(interface)):
return interface.id[len('HTML'):]
- return self.DartifyTypeName(interface.id)
+ return self.DartifyTypeName(interface.javascript_binding_name)
def RenameMember(self, interface_name, member_node, member, member_prefix='',
@@ -747,6 +740,7 @@
name = re.sub(r'^SVG', '', type_name)
name = re.sub(r'^IDB', '', name)
name = re.sub(r'^WebGL', '', name)
+ name = re.sub(r'^WebKit', '', name)
return self._CamelCaseName(name)
diff --git a/tools/dom/scripts/systemhtml.py b/tools/dom/scripts/systemhtml.py
index 75a4ffe..5473635 100644
--- a/tools/dom/scripts/systemhtml.py
+++ b/tools/dom/scripts/systemhtml.py
@@ -55,6 +55,7 @@
'HTMLTableElement.createTBody',
'IDBDatabase.transaction',
'KeyboardEvent.initKeyboardEvent',
+ 'Location.origin',
'MouseEvent.offsetX',
'MouseEvent.offsetY',
'Navigator.language',
diff --git a/tools/dom/src/CssClassSet.dart b/tools/dom/src/CssClassSet.dart
index acae69c..4ee943d 100644
--- a/tools/dom/src/CssClassSet.dart
+++ b/tools/dom/src/CssClassSet.dart
@@ -4,78 +4,28 @@
part of html;
+/** A Set that stores the CSS class names for an element. */
abstract class CssClassSet implements Set<String> {
- String toString() {
- return readClasses().join(' ');
- }
-
/**
* Adds the class [value] to the element if it is not on it, removes it if it
* is.
*/
- bool toggle(String value) {
- Set<String> s = readClasses();
- bool result = false;
- if (s.contains(value)) {
- s.remove(value);
- } else {
- s.add(value);
- result = true;
- }
- writeClasses(s);
- return result;
- }
+ bool toggle(String value);
/**
* Returns [:true:] if classes cannot be added or removed from this
* [:CssClassSet:].
*/
- bool get frozen => false;
+ bool get frozen;
- // interface Iterable - BEGIN
- Iterator<String> get iterator => readClasses().iterator;
- // interface Iterable - END
-
- // interface Collection - BEGIN
- void forEach(void f(String element)) {
- readClasses().forEach(f);
- }
-
- String join([String separator = ""]) => readClasses().join(separator);
-
- Iterable map(f(String element)) => readClasses().map(f);
-
- Iterable<String> where(bool f(String element)) => readClasses().where(f);
-
- Iterable expand(Iterable f(String element)) => readClasses().expand(f);
-
- bool every(bool f(String element)) => readClasses().every(f);
-
- bool any(bool f(String element)) => readClasses().any(f);
-
- bool get isEmpty => readClasses().isEmpty;
-
- int get length => readClasses().length;
-
- String reduce(String combine(String value, String element)) {
- return readClasses().reduce(combine);
- }
-
- dynamic fold(dynamic initialValue,
- dynamic combine(dynamic previousValue, String element)) {
- return readClasses().fold(initialValue, combine);
- }
- // interface Collection - END
-
- // interface Set - BEGIN
/**
* Determine if this element contains the class [value].
*
* This is the Dart equivalent of jQuery's
* [hasClass](http://api.jquery.com/hasClass/).
*/
- bool contains(String value) => readClasses().contains(value);
+ bool contains(String value);
/**
* Add the class [value] to element.
@@ -83,11 +33,7 @@
* This is the Dart equivalent of jQuery's
* [addClass](http://api.jquery.com/addClass/).
*/
- void add(String value) {
- // TODO - figure out if we need to do any validation here
- // or if the browser natively does enough.
- _modify((s) => s.add(value));
- }
+ void add(String value);
/**
* Remove the class [value] from element, and return true on successful
@@ -96,13 +42,7 @@
* This is the Dart equivalent of jQuery's
* [removeClass](http://api.jquery.com/removeClass/).
*/
- bool remove(Object value) {
- if (value is! String) return false;
- Set<String> s = readClasses();
- bool result = s.remove(value);
- writeClasses(s);
- return result;
- }
+ bool remove(Object value);
/**
* Add all classes specified in [iterable] to element.
@@ -110,10 +50,7 @@
* This is the Dart equivalent of jQuery's
* [addClass](http://api.jquery.com/addClass/).
*/
- void addAll(Iterable<String> iterable) {
- // TODO - see comment above about validation.
- _modify((s) => s.addAll(iterable));
- }
+ void addAll(Iterable<String> iterable);
/**
* Remove all classes specified in [iterable] from element.
@@ -121,9 +58,7 @@
* This is the Dart equivalent of jQuery's
* [removeClass](http://api.jquery.com/removeClass/).
*/
- void removeAll(Iterable<String> iterable) {
- _modify((s) => s.removeAll(iterable));
- }
+ void removeAll(Iterable<String> iterable);
/**
* Toggles all classes specified in [iterable] on element.
@@ -132,59 +67,35 @@
* remove it if it is. This is the Dart equivalent of jQuery's
* [toggleClass](http://api.jquery.com/toggleClass/).
*/
- void toggleAll(Iterable<String> iterable) {
- iterable.forEach(toggle);
+ void toggleAll(Iterable<String> iterable);
+}
+
+/**
+ * A set (union) of the CSS classes that are present in a set of elements.
+ * Implemented separately from _ElementCssClassSet for performance.
+ */
+class _MultiElementCssClassSet extends CssClassSetImpl {
+ final Iterable<Element> _elementIterable;
+ Iterable<_ElementCssClassSet> _elementCssClassSetIterable;
+
+ _MultiElementCssClassSet(this._elementIterable) {
+ _elementCssClassSetIterable = new List.from(_elementIterable).map(
+ (e) => new _ElementCssClassSet(e));
}
- void retainAll(Iterable<String> iterable) {
- _modify((s) => s.retainAll(iterable));
+ Set<String> readClasses() {
+ var s = new LinkedHashSet<String>();
+ _elementCssClassSetIterable.forEach((e) => s.addAll(e.readClasses()));
+ return s;
}
- void removeWhere(bool test(String name)) {
- _modify((s) => s.removeWhere(test));
+ void writeClasses(Set<String> s) {
+ var classes = new List.from(s).join(' ');
+ for (Element e in _elementIterable) {
+ e.$dom_className = classes;
+ }
}
- void retainWhere(bool test(String name)) {
- _modify((s) => s.retainWhere(test));
- }
-
- bool containsAll(Iterable<String> collection) =>
- readClasses().containsAll(collection);
-
- Set<String> intersection(Set<String> other) =>
- readClasses().intersection(other);
-
- Set<String> union(Set<String> other) =>
- readClasses().union(other);
-
- Set<String> difference(Set<String> other) =>
- readClasses().difference(other);
-
- String get first => readClasses().first;
- String get last => readClasses().last;
- String get single => readClasses().single;
- List<String> toList({ bool growable: true }) =>
- readClasses().toList(growable: growable);
- Set<String> toSet() => readClasses().toSet();
- Iterable<String> take(int n) => readClasses().take(n);
- Iterable<String> takeWhile(bool test(String value)) =>
- readClasses().takeWhile(test);
- Iterable<String> skip(int n) => readClasses().skip(n);
- Iterable<String> skipWhile(bool test(String value)) =>
- readClasses().skipWhile(test);
- String firstWhere(bool test(String value), { String orElse() }) =>
- readClasses().firstWhere(test, orElse: orElse);
- String lastWhere(bool test(String value), {String orElse()}) =>
- readClasses().lastWhere(test, orElse: orElse);
- String singleWhere(bool test(String value)) =>
- readClasses().singleWhere(test);
- String elementAt(int index) => readClasses().elementAt(index);
-
- void clear() {
- _modify((s) => s.clear());
- }
- // interface Set - END
-
/**
* Helper method used to modify the set of css classes on this element.
*
@@ -194,23 +105,51 @@
* After f returns, the modified set is written to the
* className property of this element.
*/
- void _modify( f(Set<String> s)) {
- Set<String> s = readClasses();
- f(s);
- writeClasses(s);
+ void modify( f(Set<String> s)) {
+ _elementCssClassSetIterable.forEach((e) => e.modify(f));
}
/**
- * Read the class names from the Element class property,
- * and put them into a set (duplicates are discarded).
- * This is intended to be overridden by specific implementations.
+ * Adds the class [value] to the element if it is not on it, removes it if it
+ * is.
*/
- Set<String> readClasses();
+ bool toggle(String value) =>
+ _modifyWithReturnValue((e) => e.toggle(value));
/**
- * Join all the elements of a set into one string and write
- * back to the element.
- * This is intended to be overridden by specific implementations.
+ * Remove the class [value] from element, and return true on successful
+ * removal.
+ *
+ * This is the Dart equivalent of jQuery's
+ * [removeClass](http://api.jquery.com/removeClass/).
*/
- void writeClasses(Set<String> s);
+ bool remove(Object value) => _modifyWithReturnValue((e) => e.remove(value));
+
+ bool _modifyWithReturnValue(f) => _elementCssClassSetIterable.fold(
+ false, (prevValue, element) => f(element) || prevValue);
+}
+
+class _ElementCssClassSet extends CssClassSetImpl {
+
+ final Element _element;
+
+ _ElementCssClassSet(this._element);
+
+ Set<String> readClasses() {
+ var s = new LinkedHashSet<String>();
+ var classname = _element.$dom_className;
+
+ for (String name in classname.split(' ')) {
+ String trimmed = name.trim();
+ if (!trimmed.isEmpty) {
+ s.add(trimmed);
+ }
+ }
+ return s;
+ }
+
+ void writeClasses(Set<String> s) {
+ List list = new List.from(s);
+ _element.$dom_className = s.join(' ');
+ }
}
diff --git a/tools/dom/src/dart2js_LocationWrapper.dart b/tools/dom/src/dart2js_LocationWrapper.dart
index f430534..c943226 100644
--- a/tools/dom/src/dart2js_LocationWrapper.dart
+++ b/tools/dom/src/dart2js_LocationWrapper.dart
@@ -44,7 +44,12 @@
}
// final String origin;
- String get origin => _get(_ptr, 'origin');
+ String get origin {
+ if (JS('bool', '("origin" in #)', _ptr)) {
+ return JS('String', '#.origin', _ptr);
+ }
+ return '${this.protocol}//${this.host}';
+ }
// String pathname;
String get pathname => _get(_ptr, 'pathname');
diff --git a/tools/dom/templates/html/impl/impl_Document.darttemplate b/tools/dom/templates/html/impl/impl_Document.darttemplate
index 73b40fa..dd42121 100644
--- a/tools/dom/templates/html/impl/impl_Document.darttemplate
+++ b/tools/dom/templates/html/impl/impl_Document.darttemplate
@@ -32,7 +32,7 @@
* For details about CSS selector syntax, see the
* [CSS selector specification](http://www.w3.org/TR/css3-selectors/).
*/
- List<Element> queryAll(String selectors) {
+ ElementList queryAll(String selectors) {
return new _FrozenElementList._wrap($dom_querySelectorAll(selectors));
}
}
diff --git a/tools/dom/templates/html/impl/impl_Element.darttemplate b/tools/dom/templates/html/impl/impl_Element.darttemplate
index 3b3ca0b..08a6b08 100644
--- a/tools/dom/templates/html/impl/impl_Element.darttemplate
+++ b/tools/dom/templates/html/impl/impl_Element.darttemplate
@@ -137,11 +137,32 @@
}
}
+/**
+ * An immutable list containing HTML elements. This list contains some
+ * additional methods for ease of CSS manipulation on a group of elements.
+ */
+abstract class ElementList<T extends Element> extends ListBase<T> {
+ /**
+ * The union of all CSS classes applied to the elements in this list.
+ *
+ * This set makes it easy to add, remove or toggle (add if not present, remove
+ * if present) the classes applied to a collection of elements.
+ *
+ * htmlList.classes.add('selected');
+ * htmlList.classes.toggle('isOnline');
+ * htmlList.classes.remove('selected');
+ */
+ CssClassSet get classes;
+
+ /** Replace the classes with `value` for every element in this list. */
+ set classes(Iterable<String> value);
+}
+
// TODO(jacobr): this is an inefficient implementation but it is hard to see
// a better option given that we cannot quite force NodeList to be an
// ElementList as there are valid cases where a NodeList JavaScript object
// contains Node objects that are not Elements.
-class _FrozenElementList<T extends Element> extends ListBase<T> {
+class _FrozenElementList<T extends Element> extends ListBase<T> implements ElementList {
final List<Node> _nodeList;
_FrozenElementList._wrap(this._nodeList);
@@ -167,30 +188,12 @@
Element get last => _nodeList.last;
Element get single => _nodeList.single;
-}
-class _ElementCssClassSet extends CssClassSet {
+ CssClassSet get classes => new _MultiElementCssClassSet(
+ _nodeList.where((e) => e is Element));
- final Element _element;
-
- _ElementCssClassSet(this._element);
-
- Set<String> readClasses() {
- var s = new LinkedHashSet<String>();
- var classname = _element.$dom_className;
-
- for (String name in classname.split(' ')) {
- String trimmed = name.trim();
- if (!trimmed.isEmpty) {
- s.add(trimmed);
- }
- }
- return s;
- }
-
- void writeClasses(Set<String> s) {
- List list = new List.from(s);
- _element.$dom_className = s.join(' ');
+ void set classes(Iterable<String> value) {
+ _nodeList.where((e) => e is Element).forEach((e) => e.classes = value);
}
}
@@ -289,7 +292,7 @@
*
* var items = element.query('.itemClassName');
*/
- List<Element> queryAll(String selectors) =>
+ ElementList queryAll(String selectors) =>
new _FrozenElementList._wrap($dom_querySelectorAll(selectors));
/**
diff --git a/tools/dom/templates/html/impl/impl_IDBFactory.darttemplate b/tools/dom/templates/html/impl/impl_IDBFactory.darttemplate
index b64989c..842b6f4 100644
--- a/tools/dom/templates/html/impl/impl_IDBFactory.darttemplate
+++ b/tools/dom/templates/html/impl/impl_IDBFactory.darttemplate
@@ -96,7 +96,7 @@
* and errors out when the request errors.
*/
Future _completeRequest(Request request) {
- var completer = new Completer();
+ var completer = new Completer.sync();
// TODO: make sure that completer.complete is synchronous as transactions
// may be committed if the result is not processed immediately.
request.onSuccess.listen((e) {
diff --git a/tools/dom/templates/html/impl/impl_Location.darttemplate b/tools/dom/templates/html/impl/impl_Location.darttemplate
new file mode 100644
index 0000000..3d69c79
--- /dev/null
+++ b/tools/dom/templates/html/impl/impl_Location.darttemplate
@@ -0,0 +1,20 @@
+// Copyright (c) 2013, 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 $LIBRARYNAME;
+
+@DocsEditable
+$(ANNOTATIONS)$(CLASS_MODIFIERS)class $CLASSNAME$EXTENDS$IMPLEMENTS$NATIVESPEC {
+$!MEMBERS
+
+$if DART2JS
+ @DomName('Location.origin')
+ String get origin {
+ if (JS('bool', '("origin" in #)', this)) {
+ return JS('String', '#.origin', this);
+ }
+ return '${this.protocol}//${this.host}';
+ }
+$endif
+}
diff --git a/tools/dom/templates/html/impl/impl_Range.darttemplate b/tools/dom/templates/html/impl/impl_Range.darttemplate
index 13ab552..1aebb9e 100644
--- a/tools/dom/templates/html/impl/impl_Range.darttemplate
+++ b/tools/dom/templates/html/impl/impl_Range.darttemplate
@@ -10,4 +10,18 @@
factory $CLASSNAME() => document.$dom_createRange();
$!MEMBERS
+
+ /**
+ * Checks if createContextualFragment is supported.
+ *
+ * See also:
+ *
+ * * [createContextualFragment]
+ */
+$if DART2JS
+ static bool get supportsCreateContextualFragment =>
+ JS('bool', '("createContextualFragment" in window.Range.prototype)');
+$else
+ static bool get supportsCreateContextualFragment => true;
+$endif
}
diff --git a/tools/dom/templates/html/impl/impl_SVGElement.darttemplate b/tools/dom/templates/html/impl/impl_SVGElement.darttemplate
index 9ab3886..8ac5997 100644
--- a/tools/dom/templates/html/impl/impl_SVGElement.darttemplate
+++ b/tools/dom/templates/html/impl/impl_SVGElement.darttemplate
@@ -4,7 +4,7 @@
part of $LIBRARYNAME;
-class _AttributeClassSet extends CssClassSet {
+class _AttributeClassSet extends CssClassSetImpl {
final Element _element;
_AttributeClassSet(this._element);
diff --git a/tools/gyp/configurations_msvs.gypi b/tools/gyp/configurations_msvs.gypi
index 4a21b2f..f2d067c 100644
--- a/tools/gyp/configurations_msvs.gypi
+++ b/tools/gyp/configurations_msvs.gypi
@@ -23,6 +23,10 @@
'LinkIncremental': '2',
'GenerateDebugInformation': 'true',
'StackReserveSize': '2097152',
+ 'AdditionalDependencies': [
+ 'advapi32.lib',
+ 'shell32.lib',
+ ],
},
},
},
@@ -46,6 +50,10 @@
'OptimizeReferences': '2',
'EnableCOMDATFolding': '2',
'StackReserveSize': '2097152',
+ 'AdditionalDependencies': [
+ 'advapi32.lib',
+ 'shell32.lib',
+ ],
},
},
},