diff --git a/pkg/dart2bytecode/docs/bytecode.md b/pkg/dart2bytecode/docs/bytecode.md index ff684aefda8..071260001f9 100644 --- a/pkg/dart2bytecode/docs/bytecode.md +++ b/pkg/dart2bytecode/docs/bytecode.md @@ -681,7 +681,7 @@ type ClosureDeclaration { type ClosureCode { UInt flags = (hasExceptionsTable, hasSourcePositions, hasLocalVariables, - capturesOnlyFinalAndSharedVars) + capturesOnlyFinalNotLateVars) UInt bytecodeSizeInBytes; Byte[bytecodeSizeInBytes] bytecodes; diff --git a/pkg/dart2bytecode/lib/bytecode_generator.dart b/pkg/dart2bytecode/lib/bytecode_generator.dart index 867291583f5..07e0cf56222 100644 --- a/pkg/dart2bytecode/lib/bytecode_generator.dart +++ b/pkg/dart2bytecode/lib/bytecode_generator.dart @@ -1671,7 +1671,7 @@ class BytecodeGenerator extends RecursiveVisitor { currentLoopDepth = 0; savedMaxSourcePositions = []; - locals = new LocalVariables(pragmaParser, node, options, staticTypeContext); + locals = new LocalVariables(node, options, staticTypeContext); locals.enterScope(node); final int startPosition; @@ -2508,14 +2508,14 @@ class BytecodeGenerator extends RecursiveVisitor { currentLoopDepth = savedLoopDepth; asyncTryBlock = savedAsyncTryBlock; - bool capturesOnlyFinalAndSharedVars = - locals.capturesOnlyFinalAndSharedVars; + bool capturesOnlyFinalNotLateVars = + locals.capturesOnlyFinalNotLateVars; locals.leaveScope(); closure.code = new ClosureCode(asm.bytecode, asm.exceptionsTable, finalizeSourcePositions(), finalizeLocalVariables(), - capturesOnlyFinalAndSharedVars); + capturesOnlyFinalNotLateVars); _popAssemblerState(); diff --git a/pkg/dart2bytecode/lib/declarations.dart b/pkg/dart2bytecode/lib/declarations.dart index 2565c20c5cf..c2aaad79b62 100644 --- a/pkg/dart2bytecode/lib/declarations.dart +++ b/pkg/dart2bytecode/lib/declarations.dart @@ -1162,13 +1162,13 @@ class ClosureCode { static const hasExceptionsTableFlag = 1 << 0; static const hasSourcePositionsFlag = 1 << 1; static const hasLocalVariablesFlag = 1 << 2; - static const capturesOnlyFinalAndSharedVarsFlag = 1 << 3; + static const capturesOnlyFinalNotLateVarsFlag = 1 << 3; final Uint8List bytecodes; final ExceptionsTable exceptionsTable; final SourcePositions? sourcePositions; final LocalVariableTable? localVariables; - final bool capturesOnlyFinalAndSharedVars; + final bool capturesOnlyFinalNotLateVars; bool get hasExceptionsTable => exceptionsTable.blocks.isNotEmpty; bool get hasSourcePositions => sourcePositions?.isNotEmpty ?? false; @@ -1178,10 +1178,10 @@ class ClosureCode { (hasExceptionsTable ? hasExceptionsTableFlag : 0) | (hasSourcePositions ? hasSourcePositionsFlag : 0) | (hasLocalVariables ? hasLocalVariablesFlag : 0) | - (capturesOnlyFinalAndSharedVars ? capturesOnlyFinalAndSharedVarsFlag : 0); + (capturesOnlyFinalNotLateVars ? capturesOnlyFinalNotLateVarsFlag : 0); ClosureCode(this.bytecodes, this.exceptionsTable, this.sourcePositions, - this.localVariables, this.capturesOnlyFinalAndSharedVars); + this.localVariables, this.capturesOnlyFinalNotLateVars); void write(BufferedWriter writer) { writer.writePackedUInt30(flags); @@ -1209,12 +1209,12 @@ class ClosureCode { final localVariables = ((flags & hasLocalVariablesFlag) != 0) ? reader.readLinkOffset() : null; - final capturesOnlyFinalAndSharedVars = - (flags & capturesOnlyFinalAndSharedVarsFlag) != 0; + final capturesOnlyFinalNotLateVars = + (flags & capturesOnlyFinalNotLateVarsFlag) != 0; return new ClosureCode( bytecodes, exceptionsTable, sourcePositions, localVariables, - capturesOnlyFinalAndSharedVars); + capturesOnlyFinalNotLateVars); } @override diff --git a/pkg/dart2bytecode/lib/local_vars.dart b/pkg/dart2bytecode/lib/local_vars.dart index 4c1f5556948..738155f458f 100644 --- a/pkg/dart2bytecode/lib/local_vars.dart +++ b/pkg/dart2bytecode/lib/local_vars.dart @@ -7,8 +7,6 @@ import 'dart:math' show min, max; import 'package:kernel/ast.dart'; import 'package:kernel/type_environment.dart'; -import 'package:vm/transformations/pragma.dart'; - import 'dbc.dart'; import 'options.dart' show BytecodeOptions; @@ -159,8 +157,8 @@ class LocalVariables { List get sortedNamedParameters => _currentFrame.sortedNamedParameters; - LocalVariables(PragmaAnnotationParser pragmaParser, Member node, this.options, this.staticTypeContext) { - final scopeBuilder = new _ScopeBuilder(pragmaParser, this); + LocalVariables(Member node, this.options, this.staticTypeContext) { + final scopeBuilder = new _ScopeBuilder(this); node.accept(scopeBuilder); final allocator = new _Allocator(this); @@ -177,8 +175,8 @@ class LocalVariables { _currentFrameInternal = _currentScopeInternal?.frame; } - bool get capturesOnlyFinalAndSharedVars => - _currentFrame.capturesOnlyFinalAndSharedVars; + bool get capturesOnlyFinalNotLateVars => + _currentFrame.capturesOnlyFinalNotLateVars; void withTemp(TreeNode node, int temp, void action()) { final old = _temps![node]; @@ -197,18 +195,17 @@ class VarDesc { final VariableDeclaration declaration; Scope scope; bool isCaptured = false; - bool _isShared; int? index; int? originalParamSlotIndex; - VarDesc(this.declaration, this.scope, this._isShared) { + VarDesc(this.declaration, this.scope) { scope.vars.add(this); } Frame get frame => scope.frame; bool get isAllocated => index != null; - bool get isFinalOrShared => declaration.isFinal || _isShared; + bool get isFinalNotLate => declaration.isFinal && !declaration.isLate; void capture() { assert(!isAllocated); @@ -250,7 +247,7 @@ class Frame { int frameSize = 0; List temporaries = []; int? contextLevelAtEntry; - bool capturesOnlyFinalAndSharedVars = true; + bool capturesOnlyFinalNotLateVars = true; Frame(this.function, this.parent); @@ -310,9 +307,7 @@ class _ScopeBuilder extends RecursiveVisitor { List _enclosingTryCatches = const []; int _loopDepth = 0; - final PragmaAnnotationParser _pragmaParser; - - _ScopeBuilder(this._pragmaParser, this.locals); + _ScopeBuilder(this.locals); List _sortNamedParameters(FunctionNode function) { final params = function.namedParameters.toList(); @@ -472,10 +467,7 @@ class _ScopeBuilder extends RecursiveVisitor { if (scope == null) { scope = _currentScope; } - final isShared = _pragmaParser - .parsedPragmas(variable.annotations) - .isNotEmpty; - final VarDesc v = new VarDesc(variable, scope, isShared); + final VarDesc v = new VarDesc(variable, scope); assert(locals._vars[variable] == null, 'Double declaring variable ${variable}!'); locals._vars[variable] = v; @@ -488,8 +480,8 @@ class _ScopeBuilder extends RecursiveVisitor { } if (v.frame != _currentFrame) { v.capture(); - if (!v.isFinalOrShared) { - _currentFrame.capturesOnlyFinalAndSharedVars = false; + if (!v.isFinalNotLate) { + _currentFrame.capturesOnlyFinalNotLateVars = false; } } } diff --git a/runtime/vm/bytecode_reader.cc b/runtime/vm/bytecode_reader.cc index 2d201a51308..8f5ef6da174 100644 --- a/runtime/vm/bytecode_reader.cc +++ b/runtime/vm/bytecode_reader.cc @@ -304,14 +304,14 @@ void BytecodeReaderHelper::ReadCode(const Function& function, (flags & ClosureCode::kHasSourcePositionsFlag) != 0; const bool has_local_variables = (flags & ClosureCode::kHasLocalVariablesFlag) != 0; - const bool does_close_over_only_final_and_shared_vars = - (flags & ClosureCode::kCapturesOnlyFinalAndSharedVarsFlag) != 0; + const bool captures_only_final_not_late_vars = + (flags & ClosureCode::kCapturesOnlyFinalNotLateVarsFlag) != 0; // Read closure bytecode and attach to closure function. closure_bytecode = ReadBytecode(pool); - closure.set_does_close_over_only_final_and_shared_vars( - does_close_over_only_final_and_shared_vars); + closure.set_captures_only_final_not_late_vars( + captures_only_final_not_late_vars); closure.AttachBytecode(closure_bytecode); ReadExceptionsTable(closure, closure_bytecode, has_exceptions_table); diff --git a/runtime/vm/bytecode_reader.h b/runtime/vm/bytecode_reader.h index bf8b221b626..eab46ce0502 100644 --- a/runtime/vm/bytecode_reader.h +++ b/runtime/vm/bytecode_reader.h @@ -308,7 +308,7 @@ class BytecodeReaderHelper : public ValueObject { static const int kHasExceptionsTableFlag = 1 << 0; static const int kHasSourcePositionsFlag = 1 << 1; static const int kHasLocalVariablesFlag = 1 << 2; - static const int kCapturesOnlyFinalAndSharedVarsFlag = 1 << 3; + static const int kCapturesOnlyFinalNotLateVarsFlag = 1 << 3; }; // Parameter flags, must be in sync with ParameterFlags constants in diff --git a/runtime/vm/ffi_callback_metadata.cc b/runtime/vm/ffi_callback_metadata.cc index 117ad4b741a..ce488e43039 100644 --- a/runtime/vm/ffi_callback_metadata.cc +++ b/runtime/vm/ffi_callback_metadata.cc @@ -399,12 +399,11 @@ void FfiCallbackMetadata::EnsureOnlyTriviallyImmutableValuesInClosure( ValidateTriviallyImmutabilityOfAnObject(zone, &obj, context.At(i)); } - if (!function.does_close_over_only_final_and_shared_vars()) { + if (!function.captures_only_final_not_late_vars()) { const String& error = String::Handle( - zone, - String::New( - "Only final and 'vm:shared' variables can be captured by isolate " - "group callbacks.")); + zone, String::New( + "Only final not-late variables can be captured by isolate " + "group callbacks.")); Exceptions::ThrowArgumentError(error); UNREACHABLE(); } diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc index d0c945453c7..277483d1229 100644 --- a/runtime/vm/object.cc +++ b/runtime/vm/object.cc @@ -8605,22 +8605,20 @@ void Function::set_awaiter_link(Function::AwaiterLink link) const { UNREACHABLE(); } -bool Function::does_close_over_only_final_and_shared_vars() const { +bool Function::captures_only_final_not_late_vars() const { if (IsClosureFunction()) { const Object& obj = Object::Handle(untag()->data()); ASSERT(!obj.IsNull()); - return ClosureData::Cast(obj).does_close_over_only_final_and_shared_vars(); + return ClosureData::Cast(obj).captures_only_final_not_late_vars(); } UNREACHABLE(); } -void Function::set_does_close_over_only_final_and_shared_vars( - bool value) const { +void Function::set_captures_only_final_not_late_vars(bool value) const { if (IsClosureFunction()) { const Object& obj = Object::Handle(untag()->data()); ASSERT(!obj.IsNull()); - ClosureData::Cast(obj).set_does_close_over_only_final_and_shared_vars( - value); + ClosureData::Cast(obj).set_captures_only_final_not_late_vars(value); return; } UNREACHABLE(); @@ -12208,13 +12206,12 @@ void ClosureData::set_awaiter_link(Function::AwaiterLink link) const { link.index); } -bool ClosureData::does_close_over_only_final_and_shared_vars() const { +bool ClosureData::captures_only_final_not_late_vars() const { return untag() ->packed_fields_.Read(); } -void ClosureData::set_does_close_over_only_final_and_shared_vars( - bool value) const { +void ClosureData::set_captures_only_final_not_late_vars(bool value) const { untag()->packed_fields_.Update( value); } diff --git a/runtime/vm/object.h b/runtime/vm/object.h index 69e40a10412..c1bd0341aea 100644 --- a/runtime/vm/object.h +++ b/runtime/vm/object.h @@ -3321,8 +3321,8 @@ class Function : public Object { (awaiter_link().depth != UntaggedClosureData::kNoAwaiterLinkDepth); } - void set_does_close_over_only_final_and_shared_vars(bool value) const; - bool does_close_over_only_final_and_shared_vars() const; + void set_captures_only_final_not_late_vars(bool value) const; + bool captures_only_final_not_late_vars() const; // Enclosing function of this local function. FunctionPtr parent_function() const; @@ -4417,8 +4417,8 @@ class ClosureData : public Object { Function::AwaiterLink awaiter_link() const; void set_awaiter_link(Function::AwaiterLink link) const; - bool does_close_over_only_final_and_shared_vars() const; - void set_does_close_over_only_final_and_shared_vars(bool value) const; + bool captures_only_final_not_late_vars() const; + void set_captures_only_final_not_late_vars(bool value) const; // Enclosing function of this local function. PRECOMPILER_WSR_FIELD_DECLARATION(Function, parent_function) diff --git a/runtime/vm/scopes.cc b/runtime/vm/scopes.cc index 3c6ab3322f5..d8d3b973085 100644 --- a/runtime/vm/scopes.cc +++ b/runtime/vm/scopes.cc @@ -462,7 +462,7 @@ ContextScopePtr LocalScope::PreserveOuterScope( LocalVariable* awaiter_link = nullptr; - bool does_capture_only_final_and_shared_vars = true; + bool captures_only_final_not_late_vars = true; // Create a descriptor for each referenced captured variable of enclosing // functions to preserve its name and its context allocation information. @@ -505,8 +505,9 @@ ContextScopePtr LocalScope::PreserveOuterScope( bool is_shared = variable->ComputeIfShared(library); context_scope.SetIsSharedAt(captured_idx, is_shared); - if (!is_shared && !variable->is_final()) { - does_capture_only_final_and_shared_vars = false; + // late final variables are not allowed, only final are. + if (!variable->is_final() || variable->is_late()) { + captures_only_final_not_late_vars = false; } captured_idx++; @@ -533,8 +534,8 @@ ContextScopePtr LocalScope::PreserveOuterScope( } if (!function.IsNull()) { - function.set_does_close_over_only_final_and_shared_vars( - does_capture_only_final_and_shared_vars); + function.set_captures_only_final_not_late_vars( + captures_only_final_not_late_vars); } return context_scope.ptr(); diff --git a/tests/ffi/isolate_group_bound_captured_local_test.dart b/tests/ffi/isolate_group_bound_captured_local_test.dart index b4cbe591853..c2ee2991f3c 100644 --- a/tests/ffi/isolate_group_bound_captured_local_test.dart +++ b/tests/ffi/isolate_group_bound_captured_local_test.dart @@ -33,13 +33,14 @@ Future testCapturedLocalVarPragmaVmShared() async { @pragma('vm:shared') // ignore: unused_local_variable int foo_result = 42; - final callback = NativeCallable.isolateGroupBound(( - int a, - int b, - ) { - foo_result += (a * b); - }); - callback.close(); + Expect.throws(() { + final callback = NativeCallable.isolateGroupBound(( + int a, + int b, + ) { + foo_result += (a * b); + }); + }, (e) => e.toString().contains("Only final not-late variables can be")); } Future testCapturedLocalVarFinal() async { diff --git a/tests/ffi/run_isolate_group_run_test.dart b/tests/ffi/run_isolate_group_run_test.dart index 5f6b7147124..5fde92ab9f4 100644 --- a/tests/ffi/run_isolate_group_run_test.dart +++ b/tests/ffi/run_isolate_group_run_test.dart @@ -22,73 +22,51 @@ import "package:expect/expect.dart"; import "run_isolate_group_run_test.dart" deferred as lib1; -var foo = 42; -var foo_no_initializer; +main(List args) { + testUpdateSharedVar(); + testReturnsConstant(); + testReturnsList(); + testReturnsNotSharedFinal(); + testUpdateSharedVarWithNoInitializer(); -@pragma('vm:shared') -var shared_foo_no_initializer; + testFailToAccessNotSharedVarWithInitializer(); + testFailToAccessNotSharedVarWithoutInitializer(); -@pragma('vm:shared') -final foo_final = 1234; + testFailToCaptureLateFinalVar(); + testCapturesFinalNotSharedVar(); -@pragma('vm:never-inline') -updateFoo() { - foo = 56; -} - -@pragma('vm:never-inline') -updateFooNoInitializer() { - foo_no_initializer = 78; -} - -class Baz { - static late final foo; -} - -@pragma('vm:never-inline') -bar() { - Baz.foo = 42; + testUpdateNotSharedStaticField(); + testUpdateSharedStringStaticVar(); + + testClosure(); + + testFailToPrint(); + + testFailToIsolateGroupRunSyncThrows(); + testIsolateCurrent(); + testFailToIsolateExit(); + testFailToIsolateSpawn(); + testStringMethodTearoff(); + testListMethodTearoff(args); + + testFailToReceivePort(); + testFailToDeferredLibrary(); + testFailToEnvironment(); + + testUserTag(); + testDoubleToString(); + testBase64Decoder(); + testRandom(); + testEncoding(); + + print("All tests completed :)"); } +/// @pragma('vm:shared') var list_length = 0; -@pragma('vm:shared') -String string_foo = ""; - -@pragma('vm:shared') -SendPort? sp; - -StringMethodTearoffTest() { - @pragma('vm:shared') - final stringTearoff = "abc".toString; - IsolateGroup.runSync(() { - stringTearoff; - }); -} - -ListMethodTearoffTest(List args) { - final listTearoff = args.insert; - Expect.throws( - () { - IsolateGroup.runSync(() { - listTearoff; - }); - }, - (e) => - e is ArgumentError && e.toString().contains("Only trivially-immutable"), - ); -} - -thefun() {} - -@pragma('vm:shared') -String default_tag = ""; - -@pragma('vm:shared') -double pi = 3.14159; - -main(List args) { +void testUpdateSharedVar() { IsolateGroup.runSync(() { final l = []; for (int i = 0; i < 100; i++) { @@ -97,67 +75,35 @@ main(List args) { list_length = l.length; }); Expect.equals(100, list_length); +} - Expect.equals(42, IsolateGroup.runSync(() => 42)); - - Expect.listEquals([1, 2, 3], IsolateGroup.runSync(() => [1, 2, 3])); +/// +@pragma('vm:shared') +final foo_final = 1234; +void testReturnsNotSharedFinal() { Expect.equals(1234, IsolateGroup.runSync(() => foo_final)); +} - IsolateGroup.runSync(() { - shared_foo_no_initializer = 2345; - }); - Expect.equals(2345, IsolateGroup.runSync(() => shared_foo_no_initializer)); +/// +void testReturnsConstant() { + Expect.equals(42, IsolateGroup.runSync(() => 42)); +} - Expect.throws( - () { - IsolateGroup.runSync(() { - throw "error"; - }); - }, - (e) => e == "error", - 'Expect thrown error', - ); +/// +void testReturnsList() { + Expect.listEquals([1, 2, 3], IsolateGroup.runSync(() => [1, 2, 3])); +} - // Documenting current limitations. - Expect.notEquals(() { - IsolateGroup.runSync(() { - return Isolate.current; - }); - }, Isolate.current); +/// +var foo_no_initializer; - Expect.throws( - () { - IsolateGroup.runSync(() { - print('42'); - }); - }, - (e) => e is Error && e.toString().contains("AccessError"), - 'Expect error printing', - ); - - updateFoo(); - Expect.throws( - () { - IsolateGroup.runSync(() { - return foo; - }); - }, - (e) => e is Error && e.toString().contains("AccessError"), - 'Expect error accessing', - ); - - Expect.throws( - () { - IsolateGroup.runSync(() { - foo = 123; - }); - }, - (e) => e is Error && e.toString().contains("AccessError"), - 'Expect error accessing', - ); - Expect.equals(56, foo); +@pragma('vm:never-inline') +updateFooNoInitializer() { + foo_no_initializer = 78; +} +void testFailToAccessNotSharedVarWithoutInitializer() { updateFooNoInitializer(); Expect.throws( () { @@ -179,34 +125,156 @@ main(List args) { 'Expect error accessing', ); Expect.equals(78, foo_no_initializer); +} - { - bar(); - Expect.equals(42, Baz.foo); - } +/// +void testFailToCaptureLateFinalVar() { + late final int late_final_var; + late_final_var = 12; + Expect.throws(() { + IsolateGroup.runSync(() { + return late_final_var; + }); + }, (e) => e is Error && e.toString().contains("Only final")); +} - IsolateGroup.runSync(() { - string_foo = "foo bar"; - }); - Expect.equals("foo bar", string_foo); +/// +@pragma('vm:never-inline') +calculateTwelve() => 12; + +void testCapturesFinalNotSharedVar() { + final int late_final_var = calculateTwelve(); + Expect.equals(late_final_var, IsolateGroup.runSync(() => late_final_var)); +} + +/// +var foo = 42; + +@pragma('vm:never-inline') +updateFoo() { + foo = 56; +} + +void testFailToAccessNotSharedVarWithInitializer() { + updateFoo(); + Expect.throws( + () { + IsolateGroup.runSync(() { + return foo; + }); + }, + (e) => e is Error && e.toString().contains("AccessError"), + 'Expect error accessing', + ); Expect.throws( () { IsolateGroup.runSync(() { - ReceivePort(); + foo = 123; }); }, - (e) => - e is ArgumentError && - e.toString().contains("Only available when running in context"), + (e) => e is Error && e.toString().contains("AccessError"), + 'Expect error accessing', ); + Expect.equals(56, foo); +} +/// +@pragma('vm:shared') +var shared_foo_no_initializer; + +void testUpdateSharedVarWithNoInitializer() { + IsolateGroup.runSync(() { + shared_foo_no_initializer = 2345; + }); + Expect.equals(2345, IsolateGroup.runSync(() => shared_foo_no_initializer)); +} + +/// +class Baz { + static late final foo; +} + +@pragma('vm:never-inline') +updateBazFoo() { + Baz.foo = 42; +} + +void testUpdateNotSharedStaticField() { + updateBazFoo(); + Expect.equals(42, Baz.foo); +} + +/// +@pragma('vm:shared') +String string_foo = ""; + +void testUpdateSharedStringStaticVar() { + IsolateGroup.runSync(() { + string_foo = "foo bar"; + }); + Expect.equals("foo bar", string_foo); +} + +/// +@pragma('vm:never-inline') +@pragma('vm:shared') +var closure = () { + return 42; +}; +void testClosure() { + final result = IsolateGroup.runSync(() { + return closure(); + }); + Expect.equals(42, result); +} + +/// +void testFailToPrint() { + Expect.throws( + () { + IsolateGroup.runSync(() { + print('42'); + }); + }, + (e) => e is Error && e.toString().contains("AccessError"), + 'Expect error printing', + ); +} + +/// +void testFailToIsolateGroupRunSyncThrows() { + Expect.throws( + () { + IsolateGroup.runSync(() { + throw "error"; + }); + }, + (e) => e == "error", + 'Expect thrown error', + ); +} + +/// +void testIsolateCurrent() { + Expect.notEquals(() { + IsolateGroup.runSync(() { + return Isolate.current; + }); + }, Isolate.current); +} + +/// +void testFailToIsolateExit() { Expect.throws(() { IsolateGroup.runSync(() { Isolate.exit(); }); }, (e) => e.toString().contains("Attempt to access isolate static field")); +} +/// +void testFailToIsolateSpawn() { Expect.throws(() { IsolateGroup.runSync(() { Isolate.spawn((_) {}, null); @@ -218,44 +286,96 @@ main(List args) { Isolate.spawnUri(Uri.parse("http://127.0.0.1"), [], (_) {}); }); }, (e) => e.toString().contains("Attempt to access isolate static field")); +} - StringMethodTearoffTest(); - ListMethodTearoffTest(args); +/// +testStringMethodTearoff() { + @pragma('vm:shared') + final stringTearoff = "abc".toString; + IsolateGroup.runSync(() { + stringTearoff; + }); +} - { - final rp = ReceivePort(); - Expect.throws( - () { - IsolateGroup.runSync(() { - sp = rp.sendPort; - }); - }, - (e) => - e is ArgumentError && - e.toString().contains("Only trivially-immutable"), - ); - rp.close(); - } +/// +testListMethodTearoff(List args) { + final listTearoff = args.insert; + Expect.throws( + () { + IsolateGroup.runSync(() { + listTearoff; + }); + }, + (e) => + e is ArgumentError && e.toString().contains("Only trivially-immutable"), + ); +} - // deferred libraries can't be used from isolate group callbacks. +/// +@pragma('vm:shared') +SendPort? sp; + +void testFailToReceivePort() { + Expect.throws( + () { + IsolateGroup.runSync(() { + ReceivePort(); + }); + }, + (e) => + e is ArgumentError && + e.toString().contains("Only available when running in context"), + ); + + final rp = ReceivePort(); + Expect.throws( + () { + IsolateGroup.runSync(() { + sp = rp.sendPort; + }); + }, + (e) => + e is ArgumentError && e.toString().contains("Only trivially-immutable"), + ); + rp.close(); +} + +/// +thefun() {} + +void testFailToDeferredLibrary() { Expect.throws(() { IsolateGroup.runSync(() { lib1.thefun(); }); }, (e) => e is ArgumentError && e.toString().contains("Only available when")); +} - // environment can't be accessed from isolate group callbacks. +/// +void testFailToEnvironment() { Expect.throws(() { IsolateGroup.runSync(() { new bool.hasEnvironment("Anything"); }); }, (e) => e is ArgumentError && e.toString().contains("Only available when")); +} +/// +@pragma('vm:shared') +String default_tag = ""; + +void testUserTag() { IsolateGroup.runSync(() { default_tag = UserTag.defaultTag.toString(); }); Expect.notEquals("", default_tag); +} +/// +@pragma('vm:shared') +double pi = 3.14159; + +void testDoubleToString() { final result = IsolateGroup.runSync(() { return pi.toString(); }); @@ -264,18 +384,27 @@ main(List args) { return identical(pi.toString(), pi.toString()); }); Expect.isTrue(resultIdentical); +} +/// +void testBase64Decoder() { Expect.listEquals( "abcdefghijklmnopqrstuvwxyz".codeUnits, IsolateGroup.runSync(() { return Base64Decoder().convert("YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXo="); }), ); +} +/// +void testRandom() { IsolateGroup.runSync(() { Random().nextInt(10); }); +} +/// +void testEncoding() { Expect.listEquals( [0x31, 0x32, 0x33, 0x61, 0x62, 0x63], IsolateGroup.runSync( @@ -290,6 +419,4 @@ main(List args) { utf8, IsolateGroup.runSync(() => Encoding.getByName("utf-8")), ); - - print("All tests completed :)"); }