Version 3.15.8
Enforced stack allocation of TryCatch blocks. (issue 2166,chromium:152389)
Fixed external exceptions in external try-catch handlers. (issue 2166)
Activated incremental code flushing by default.
Performance and stability improvements on all platforms.
git-svn-id: http://v8.googlecode.com/svn/trunk@13133 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc
index aa85e2a..1610dcd 100644
--- a/test/cctest/test-api.cc
+++ b/test/cctest/test-api.cc
@@ -3720,6 +3720,30 @@
}
+static void TryCatchNestedHelper(int depth) {
+ if (depth > 0) {
+ v8::TryCatch try_catch;
+ try_catch.SetVerbose(true);
+ TryCatchNestedHelper(depth - 1);
+ CHECK(try_catch.HasCaught());
+ try_catch.ReThrow();
+ } else {
+ v8::ThrowException(v8_str("back"));
+ }
+}
+
+
+TEST(TryCatchNested) {
+ v8::V8::Initialize();
+ v8::HandleScope scope;
+ LocalContext context;
+ v8::TryCatch try_catch;
+ TryCatchNestedHelper(5);
+ CHECK(try_catch.HasCaught());
+ CHECK_EQ(0, strcmp(*v8::String::Utf8Value(try_catch.Exception()), "back"));
+}
+
+
THREADED_TEST(Equality) {
v8::HandleScope scope;
LocalContext context;
@@ -8069,12 +8093,8 @@
Local<ObjectTemplate> proto = t->PrototypeTemplate();
Local<ObjectTemplate> instance = t->InstanceTemplate();
- // Only allow calls of f on instances of t.
- Local<v8::Signature> signature = v8::Signature::New(t);
proto->Set(v8_str("f"),
- v8::FunctionTemplate::New(ShadowFunctionCallback,
- Local<Value>(),
- signature));
+ v8::FunctionTemplate::New(ShadowFunctionCallback, Local<Value>()));
proto->Set(v8_str("x"), v8_num(12));
instance->SetAccessor(v8_str("y"), ShadowYGetter, ShadowYSetter);
@@ -9939,6 +9959,7 @@
v8::Signature::New(fun_templ));
v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
proto_templ->Set(v8_str("method"), method_templ);
+ fun_templ->SetHiddenPrototype(true);
v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
NULL, NULL, NULL, NULL,
@@ -9969,6 +9990,7 @@
v8::Signature::New(fun_templ));
v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
proto_templ->Set(v8_str("method"), method_templ);
+ fun_templ->SetHiddenPrototype(true);
v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
NULL, NULL, NULL, NULL,
@@ -10005,6 +10027,7 @@
v8::Signature::New(fun_templ));
v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
proto_templ->Set(v8_str("method"), method_templ);
+ fun_templ->SetHiddenPrototype(true);
v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
NULL, NULL, NULL, NULL,
@@ -10041,6 +10064,7 @@
v8::Signature::New(fun_templ));
v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
proto_templ->Set(v8_str("method"), method_templ);
+ fun_templ->SetHiddenPrototype(true);
v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
NULL, NULL, NULL, NULL,
@@ -10080,6 +10104,7 @@
v8::Signature::New(fun_templ));
v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
proto_templ->Set(v8_str("method"), method_templ);
+ fun_templ->SetHiddenPrototype(true);
v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
NULL, NULL, NULL, NULL,
@@ -10142,6 +10167,7 @@
v8::Signature::New(fun_templ));
v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
proto_templ->Set(v8_str("method"), method_templ);
+ fun_templ->SetHiddenPrototype(true);
v8::Handle<v8::ObjectTemplate> templ(fun_templ->InstanceTemplate());
CHECK(!templ.IsEmpty());
LocalContext context;
@@ -10169,6 +10195,7 @@
v8::Signature::New(fun_templ));
v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
proto_templ->Set(v8_str("method"), method_templ);
+ fun_templ->SetHiddenPrototype(true);
v8::Handle<v8::ObjectTemplate> templ(fun_templ->InstanceTemplate());
CHECK(!templ.IsEmpty());
LocalContext context;
@@ -10201,6 +10228,7 @@
v8::Signature::New(fun_templ));
v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
proto_templ->Set(v8_str("method"), method_templ);
+ fun_templ->SetHiddenPrototype(true);
v8::Handle<v8::ObjectTemplate> templ(fun_templ->InstanceTemplate());
CHECK(!templ.IsEmpty());
LocalContext context;
@@ -10227,6 +10255,42 @@
CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
}
+THREADED_TEST(CallICFastApi_SimpleSignature_TypeError) {
+ v8::HandleScope scope;
+ v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
+ v8::Handle<v8::FunctionTemplate> method_templ =
+ v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
+ v8_str("method_data"),
+ v8::Signature::New(fun_templ));
+ v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
+ proto_templ->Set(v8_str("method"), method_templ);
+ fun_templ->SetHiddenPrototype(true);
+ v8::Handle<v8::ObjectTemplate> templ(fun_templ->InstanceTemplate());
+ CHECK(!templ.IsEmpty());
+ LocalContext context;
+ v8::Handle<v8::Function> fun = fun_templ->GetFunction();
+ GenerateSomeGarbage();
+ context->Global()->Set(v8_str("o"), fun->NewInstance());
+ v8::TryCatch try_catch;
+ CompileRun(
+ "o.foo = 17;"
+ "var receiver = {};"
+ "receiver.__proto__ = o;"
+ "var result = 0;"
+ "var saved_result = 0;"
+ "for (var i = 0; i < 100; i++) {"
+ " result = receiver.method(41);"
+ " if (i == 50) {"
+ " saved_result = result;"
+ " receiver = Object.create(receiver);"
+ " }"
+ "}");
+ CHECK(try_catch.HasCaught());
+ CHECK_EQ(v8_str("TypeError: Illegal invocation"),
+ try_catch.Exception()->ToString());
+ CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
+}
+
v8::Handle<Value> keyed_call_ic_function;
diff --git a/test/cctest/test-cpu-profiler.cc b/test/cctest/test-cpu-profiler.cc
index 589e6d8..b10e688 100644
--- a/test/cctest/test-cpu-profiler.cc
+++ b/test/cctest/test-cpu-profiler.cc
@@ -5,7 +5,6 @@
#include "v8.h"
#include "cpu-profiler-inl.h"
#include "cctest.h"
-#include "platform.h"
#include "../include/v8-profiler.h"
using i::CodeEntry;
@@ -21,7 +20,7 @@
TEST(StartStop) {
CpuProfilesCollection profiles;
ProfileGenerator generator(&profiles);
- ProfilerEventsProcessor processor(&generator, NULL, 1000);
+ ProfilerEventsProcessor processor(&generator);
processor.Start();
processor.Stop();
processor.Join();
@@ -39,13 +38,11 @@
return reinterpret_cast<i::Address>(n);
}
-static void AddTickSampleEvent(ProfilerEventsProcessor* processor,
- i::Address frame1,
- i::Address frame2 = NULL,
- i::Address frame3 = NULL) {
- i::TickSample* sample;
- i::OS::Sleep(20);
- while ((sample = processor->StartTickSampleEvent()) == NULL) i::OS::Sleep(20);
+static void EnqueueTickSampleEvent(ProfilerEventsProcessor* proc,
+ i::Address frame1,
+ i::Address frame2 = NULL,
+ i::Address frame3 = NULL) {
+ i::TickSample* sample = proc->TickSampleEvent();
sample->pc = frame1;
sample->tos = frame1;
sample->frames_count = 0;
@@ -57,7 +54,6 @@
sample->stack[1] = frame3;
sample->frames_count = 2;
}
- processor->FinishTickSampleEvent();
}
namespace {
@@ -85,7 +81,7 @@
CpuProfilesCollection profiles;
profiles.StartProfiling("", 1);
ProfileGenerator generator(&profiles);
- ProfilerEventsProcessor processor(&generator, NULL, 1000);
+ ProfilerEventsProcessor processor(&generator);
processor.Start();
// Enqueue code creation events.
@@ -112,8 +108,8 @@
processor.CodeMoveEvent(ToAddress(0x1400), ToAddress(0x1500));
processor.CodeCreateEvent(i::Logger::STUB_TAG, 3, ToAddress(0x1600), 0x10);
processor.CodeCreateEvent(i::Logger::STUB_TAG, 4, ToAddress(0x1605), 0x10);
- // Add a tick event to enable code events processing.
- AddTickSampleEvent(&processor, ToAddress(0x1000));
+ // Enqueue a tick event to enable code events processing.
+ EnqueueTickSampleEvent(&processor, ToAddress(0x1000));
processor.Stop();
processor.Join();
@@ -146,7 +142,7 @@
CpuProfilesCollection profiles;
profiles.StartProfiling("", 1);
ProfileGenerator generator(&profiles);
- ProfilerEventsProcessor processor(&generator, NULL, 1000);
+ ProfilerEventsProcessor processor(&generator);
processor.Start();
processor.CodeCreateEvent(i::Logger::BUILTIN_TAG,
@@ -158,12 +154,12 @@
"ddd",
ToAddress(0x1400),
0x80);
- AddTickSampleEvent(&processor, ToAddress(0x1210));
- AddTickSampleEvent(&processor, ToAddress(0x1305), ToAddress(0x1220));
- AddTickSampleEvent(&processor,
- ToAddress(0x1404),
- ToAddress(0x1305),
- ToAddress(0x1230));
+ EnqueueTickSampleEvent(&processor, ToAddress(0x1210));
+ EnqueueTickSampleEvent(&processor, ToAddress(0x1305), ToAddress(0x1220));
+ EnqueueTickSampleEvent(&processor,
+ ToAddress(0x1404),
+ ToAddress(0x1305),
+ ToAddress(0x1230));
processor.Stop();
processor.Join();
@@ -236,7 +232,7 @@
CpuProfilesCollection profiles;
profiles.StartProfiling("", 1);
ProfileGenerator generator(&profiles);
- ProfilerEventsProcessor processor(&generator, NULL, 1000);
+ ProfilerEventsProcessor processor(&generator);
processor.Start();
processor.CodeCreateEvent(i::Logger::BUILTIN_TAG,
@@ -244,14 +240,13 @@
ToAddress(0x1200),
0x80);
- i::TickSample* sample = processor.StartTickSampleEvent();
+ i::TickSample* sample = processor.TickSampleEvent();
sample->pc = ToAddress(0x1200);
sample->tos = 0;
sample->frames_count = i::TickSample::kMaxFramesCount;
for (int i = 0; i < sample->frames_count; ++i) {
sample->stack[i] = ToAddress(0x1200);
}
- processor.FinishTickSampleEvent();
processor.Stop();
processor.Join();
diff --git a/test/cctest/test-heap.cc b/test/cctest/test-heap.cc
index f5bac2c..dc5247d 100644
--- a/test/cctest/test-heap.cc
+++ b/test/cctest/test-heap.cc
@@ -1116,8 +1116,10 @@
// Bump the code age so that flushing is triggered while the function
// object is still located in new-space.
const int kAgingThreshold = 6;
- function->shared()->set_code_age(kAgingThreshold);
- function2->shared()->set_code_age(kAgingThreshold);
+ for (int i = 0; i < kAgingThreshold; i++) {
+ function->shared()->code()->MakeOlder(static_cast<MarkingParity>(i % 2));
+ function2->shared()->code()->MakeOlder(static_cast<MarkingParity>(i % 2));
+ }
// Simulate incremental marking so that the functions are enqueued as
// code flushing candidates. Then kill one of the functions. Finally
@@ -1166,7 +1168,9 @@
// Bump the code age so that flushing is triggered.
const int kAgingThreshold = 6;
- function->shared()->set_code_age(kAgingThreshold);
+ for (int i = 0; i < kAgingThreshold; i++) {
+ function->shared()->code()->MakeOlder(static_cast<MarkingParity>(i % 2));
+ }
// Simulate incremental marking so that the function is enqueued as
// code flushing candidate.
diff --git a/test/mjsunit/debug-liveedit-compile-error.js b/test/mjsunit/debug-liveedit-compile-error.js
new file mode 100644
index 0000000..2fd6aed
--- /dev/null
+++ b/test/mjsunit/debug-liveedit-compile-error.js
@@ -0,0 +1,60 @@
+// Copyright 2012 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Flags: --expose-debug-as debug
+// Get the Debug object exposed from the debug context global object.
+
+Debug = debug.Debug
+
+eval("var something1 = 25; \n"
+ + " function ChooseAnimal() { return 'Cat'; } \n"
+ + " ChooseAnimal.Helper = function() { return 'Help!'; }\n");
+
+assertEquals("Cat", ChooseAnimal());
+
+var script = Debug.findScript(ChooseAnimal);
+
+var orig_animal = "Cat";
+var patch_pos = script.source.indexOf(orig_animal);
+var new_animal_patch = "Cap' + ) + 'bara";
+
+var change_log = new Array();
+var caught_exception = null;
+try {
+ Debug.LiveEdit.TestApi.ApplySingleChunkPatch(script, patch_pos,
+ orig_animal.length, new_animal_patch, change_log);
+} catch (e) {
+ caught_exception = e;
+}
+
+assertNotNull(caught_exception);
+assertEquals("Unexpected token )",
+ caught_exception.details.syntaxErrorMessage);
+
+assertEquals(2, caught_exception.details.position.start.line);
+
+
diff --git a/test/mjsunit/debug-set-variable-value.js b/test/mjsunit/debug-set-variable-value.js
new file mode 100644
index 0000000..dac8861
--- /dev/null
+++ b/test/mjsunit/debug-set-variable-value.js
@@ -0,0 +1,176 @@
+// Copyright 2012 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Flags: --expose-debug-as debug
+
+// Get the Debug object exposed from the debug context global object.
+var Debug = debug.Debug;
+
+// Accepts a function/closure 'fun' that must have a debugger statement inside.
+// A variable 'variable_name' must be initialized before debugger statement
+// and returned after the statement. The test will alter variable value when
+// on debugger statement and check that returned value reflects the change.
+function RunPauseTest(scope_number, variable_name, expected_new_value, fun) {
+ var old_value = fun();
+
+ var listener_delegate;
+ var listener_called = false;
+ var exception = null;
+
+ function listener_delegate(exec_state) {
+ var scope = exec_state.frame(0).scope(scope_number);
+ scope.setVariableValue(variable_name, expected_new_value);
+ }
+
+ function listener(event, exec_state, event_data, data) {
+ try {
+ if (event == Debug.DebugEvent.Break) {
+ listener_called = true;
+ listener_delegate(exec_state);
+ }
+ } catch (e) {
+ exception = e;
+ }
+ }
+
+ // Add the debug event listener.
+ Debug.setListener(listener);
+
+ var actual_new_value;
+ try {
+ actual_new_value = fun();
+ } finally {
+ Debug.setListener(null);
+ }
+
+ if (exception != null) {
+ assertUnreachable("Exception: " + exception);
+ }
+ assertTrue(listener_called);
+
+ assertTrue(old_value != actual_new_value);
+ assertTrue(expected_new_value == actual_new_value);
+}
+
+// Accepts a closure 'fun' that returns a variable from it's outer scope.
+// The test changes the value of variable via the handle to function and checks
+// that the return value changed accordingly.
+function RunClosureTest(scope_number, variable_name, expected_new_value, fun) {
+ var old_value = fun();
+
+ var fun_mirror = Debug.MakeMirror(fun);
+
+ var scope = fun_mirror.scope(scope_number);
+ scope.setVariableValue(variable_name, expected_new_value);
+
+ var actual_new_value = fun();
+
+ assertTrue(old_value != actual_new_value);
+ assertTrue(expected_new_value == actual_new_value);
+}
+
+// Test changing variable value when in pause
+RunPauseTest(1, 'v1', 5, (function Factory() {
+ var v1 = 'cat';
+ return function() {
+ debugger;
+ return v1;
+ }
+})());
+
+RunPauseTest(1, 'v2', 11, (function Factory(v2) {
+ return function() {
+ debugger;
+ return v2;
+ }
+})('dog'));
+
+RunPauseTest(3, 'foo', 77, (function Factory() {
+ var foo = "capybara";
+ return (function() {
+ var bar = "fish";
+ try {
+ throw {name: "test exception"};
+ } catch (e) {
+ return function() {
+ debugger;
+ bar = "beast";
+ return foo;
+ }
+ }
+ })();
+})());
+
+
+
+// Test changing variable value in closure by handle
+RunClosureTest(0, 'v1', 5, (function Factory() {
+ var v1 = 'cat';
+ return function() {
+ return v1;
+ }
+})());
+
+RunClosureTest(0, 'v2', 11, (function Factory(v2) {
+ return function() {
+ return v2;
+ }
+})('dog'));
+
+RunClosureTest(2, 'foo', 77, (function Factory() {
+ var foo = "capybara";
+ return (function() {
+ var bar = "fish";
+ try {
+ throw {name: "test exception"};
+ } catch (e) {
+ return function() {
+ bar = "beast";
+ return foo;
+ }
+ }
+ })();
+})());
+
+
+// Test value description protocol JSON
+assertEquals(true, Debug.TestApi.CommandProcessorResolveValue({value: true}));
+
+assertSame(null, Debug.TestApi.CommandProcessorResolveValue({type: "null"}));
+assertSame(undefined,
+ Debug.TestApi.CommandProcessorResolveValue({type: "undefined"}));
+
+assertSame("123", Debug.TestApi.CommandProcessorResolveValue(
+ {type: "string", stringDescription: "123"}));
+assertSame(123, Debug.TestApi.CommandProcessorResolveValue(
+ {type: "number", stringDescription: "123"}));
+
+assertSame(Number, Debug.TestApi.CommandProcessorResolveValue(
+ {handle: Debug.MakeMirror(Number).handle()}));
+assertSame(RunClosureTest, Debug.TestApi.CommandProcessorResolveValue(
+ {handle: Debug.MakeMirror(RunClosureTest).handle()}));
+
diff --git a/test/mjsunit/harmony/object-observe.js b/test/mjsunit/harmony/object-observe.js
index 474bfcd..d88c24e 100644
--- a/test/mjsunit/harmony/object-observe.js
+++ b/test/mjsunit/harmony/object-observe.js
@@ -584,8 +584,7 @@
Object.deliverChangeRecords(observer.callback);
observer.assertCallbackRecords([
{ object: arr, name: '3', type: 'deleted', oldValue: 'd' },
- // TODO(adamk): oldValue should not be present below
- { object: arr, name: '2', type: 'deleted', oldValue: undefined },
+ { object: arr, name: '2', type: 'deleted' },
{ object: arr, name: 'length', type: 'updated', oldValue: 4 },
{ object: arr, name: '1', type: 'deleted', oldValue: 'b' },
{ object: arr, name: 'length', type: 'updated', oldValue: 2 },
diff --git a/test/mjsunit/string-split.js b/test/mjsunit/string-split.js
index d8412f0..1308244 100644
--- a/test/mjsunit/string-split.js
+++ b/test/mjsunit/string-split.js
@@ -66,6 +66,23 @@
assertArrayEquals(["a", "b", "c"], "abc".split(/(?=.)/));
+assertArrayEquals(["Wenige", "sind", "auserwählt."],
+ "Wenige sind auserwählt.".split(" "));
+
+assertArrayEquals([], "Wenige sind auserwählt.".split(" ", 0));
+
+assertArrayEquals(["Wenige"], "Wenige sind auserwählt.".split(" ", 1));
+
+assertArrayEquals(["Wenige", "sind"], "Wenige sind auserwählt.".split(" ", 2));
+
+assertArrayEquals(["Wenige", "sind", "auserwählt."],
+ "Wenige sind auserwählt.".split(" ", 3));
+
+assertArrayEquals(["Wenige sind auserw", "hlt."],
+ "Wenige sind auserwählt.".split("ä"));
+
+assertArrayEquals(["Wenige sind ", "."],
+ "Wenige sind auserwählt.".split("auserwählt"));
/* "ab".split(/((?=.))/)
*