[dart2wasm, standalone] Migrate String
This migrates the `String` implementation from using JS interop to explicit host imports for the standalone target. This moves a few helper methods shared between the JS and standalone targets to `dart:_string_helper`. This also moves the embedder regexp implementation to `dart:_string` to be able to access internals in some string methods (similar to how the JS implementation special-cases `JSSyntaxRegExp`). This removes the final real use of JS-interop in the standalone target. So, we can: - Remove internal JS helper libraries from the target. - Skip JS-interop transformations in the compiler. - Stop emitting a helper module and support script. Because `js_interop` is imported in `dart:_wasm`, we can't remove the library entirely. This replaces it with a stub to avoid compilation errors, a proper removal is tracked in dartbug.com/63166. Change-Id: Ide495c210c3a272438deebf8fe4f3f44ba314ffa Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/501960 Reviewed-by: Martin Kustermann <kustermann@google.com> Reviewed-by: Kevin Moore <kevmoo@google.com> Commit-Queue: Martin Kustermann <kustermann@google.com>
This commit is contained in:
committed by
dart-scoped@luci-project-accounts.iam.gserviceaccount.com
parent
3de2ddee09
commit
58f9d88fb2
@@ -324,20 +324,22 @@ class AsyncStateMachineCodeGenerator extends StateMachineCodeGenerator {
|
||||
b.local_set(exceptionLocal);
|
||||
callCompleteError();
|
||||
|
||||
// Handle JS exceptions.
|
||||
b.catch_legacy(translator.getJsExceptionTag(b.moduleBuilder));
|
||||
if (!translator.options.standalone) {
|
||||
// Handle JS exceptions.
|
||||
b.catch_legacy(translator.getJsExceptionTag(b.moduleBuilder));
|
||||
|
||||
final jsExceptionLocal = addLocal(w.RefType.extern(nullable: true));
|
||||
b.local_tee(jsExceptionLocal);
|
||||
final jsExceptionLocal = addLocal(w.RefType.extern(nullable: true));
|
||||
b.local_tee(jsExceptionLocal);
|
||||
|
||||
call(translator.boxJsException.reference);
|
||||
b.local_tee(exceptionLocal); // ref null #Top
|
||||
call(translator.boxJsException.reference);
|
||||
b.local_tee(exceptionLocal); // ref null #Top
|
||||
|
||||
b.local_get(jsExceptionLocal);
|
||||
call(translator.jsExceptionStackTrace.reference);
|
||||
b.local_set(stackTraceLocal);
|
||||
b.local_get(jsExceptionLocal);
|
||||
call(translator.jsExceptionStackTrace.reference);
|
||||
b.local_set(stackTraceLocal);
|
||||
|
||||
callCompleteError();
|
||||
callCompleteError();
|
||||
}
|
||||
|
||||
b.end(); // try
|
||||
|
||||
|
||||
@@ -1129,9 +1129,11 @@ abstract class AstCodeGenerator
|
||||
b.rethrow_(tryBlock);
|
||||
|
||||
// Handle JS exceptions.
|
||||
b.catch_legacy(translator.getJsExceptionTag(b.moduleBuilder));
|
||||
translateStatement(node.finalizer);
|
||||
b.rethrow_(tryBlock);
|
||||
if (!translator.options.standalone) {
|
||||
b.catch_legacy(translator.getJsExceptionTag(b.moduleBuilder));
|
||||
translateStatement(node.finalizer);
|
||||
b.rethrow_(tryBlock);
|
||||
}
|
||||
|
||||
b.end(); // tryBlock
|
||||
|
||||
@@ -6201,6 +6203,12 @@ class LambdaCallTarget extends CallTarget {
|
||||
/// Note that the guard type can be nullable, but the value for the exception
|
||||
/// needs to be non-null regardless of the guard type, as per Dart semantics.
|
||||
bool guardCanMatchJSException(Translator translator, DartType guard) {
|
||||
if (translator.options.standalone) {
|
||||
// Standalone mode doesn't run in a JavaScript context and doesn't have JS
|
||||
// exceptions.
|
||||
return false;
|
||||
}
|
||||
|
||||
return translator.typeEnvironment.isSubtypeOf(
|
||||
InterfaceType(translator.jsValueClass, Nullability.nonNullable),
|
||||
guard.extensionTypeErasure,
|
||||
|
||||
@@ -425,11 +425,13 @@ Future<CompilationResult> _runTfaPhase(
|
||||
);
|
||||
}
|
||||
|
||||
js.performJSInteropTransformations(
|
||||
component.libraries,
|
||||
coreTypes,
|
||||
classHierarchy,
|
||||
);
|
||||
if (!options.translatorOptions.standalone) {
|
||||
js.performJSInteropTransformations(
|
||||
component.libraries,
|
||||
coreTypes,
|
||||
classHierarchy,
|
||||
);
|
||||
}
|
||||
|
||||
final librariesToTransform = component.libraries;
|
||||
final constantEvaluator = ConstantEvaluator(
|
||||
@@ -635,25 +637,30 @@ Future<CompilationResult> _runCodegenPhase(
|
||||
});
|
||||
await Future.wait(writeFutures);
|
||||
|
||||
final jsRuntimeFinalizer = js.RuntimeFinalizer(
|
||||
coreTypes,
|
||||
translator.interopMemberNamer,
|
||||
);
|
||||
if (!options.translatorOptions.standalone) {
|
||||
final jsRuntimeFinalizer = js.RuntimeFinalizer(
|
||||
coreTypes,
|
||||
translator.interopMemberNamer,
|
||||
);
|
||||
|
||||
final jsRuntime = jsRuntimeFinalizer.generate(
|
||||
moduleOutputData.mainModule.moduleImportName,
|
||||
translator.functions.translatedProcedures,
|
||||
translator.internalizedStringsForJSRuntime,
|
||||
translator.options.requireJsStringBuiltin,
|
||||
translator.options.enableDeferredLoading ||
|
||||
translator.options.enableMultiModuleStressTestMode,
|
||||
);
|
||||
final jsRuntime = jsRuntimeFinalizer.generate(
|
||||
moduleOutputData.mainModule.moduleImportName,
|
||||
translator.functions.translatedProcedures,
|
||||
translator.internalizedStringsForJSRuntime,
|
||||
translator.options.requireJsStringBuiltin,
|
||||
translator.options.enableDeferredLoading ||
|
||||
translator.options.enableMultiModuleStressTestMode,
|
||||
);
|
||||
|
||||
final supportJs = _generateSupportJs(options.translatorOptions);
|
||||
final supportJs = _generateSupportJs(options.translatorOptions);
|
||||
|
||||
final deferredMapFile = options.deferredMapUri;
|
||||
if (deferredMapFile != null) {
|
||||
await writeDeferredMapFile(component, coreTypes, options, loadingMap);
|
||||
final deferredMapFile = options.deferredMapUri;
|
||||
if (deferredMapFile != null) {
|
||||
await writeDeferredMapFile(component, coreTypes, options, loadingMap);
|
||||
}
|
||||
|
||||
await ioManager.writeJsRuntime(jsRuntime);
|
||||
await ioManager.writeSupportJs(supportJs);
|
||||
}
|
||||
|
||||
final wasmOutputFilename = path.basename(options.outputFile);
|
||||
@@ -666,9 +673,6 @@ Future<CompilationResult> _runCodegenPhase(
|
||||
)
|
||||
.toSet();
|
||||
|
||||
await ioManager.writeJsRuntime(jsRuntime);
|
||||
await ioManager.writeSupportJs(supportJs);
|
||||
|
||||
if (options.recordedUsesFile != null) {
|
||||
record_use.LoadingUnit loadingUnitForNode(TreeNode node) {
|
||||
while (node is! NamedNode) {
|
||||
|
||||
@@ -386,7 +386,7 @@ class ExceptionHandlerStack {
|
||||
canHandleJSExceptions |= handler.canHandleJSExceptions;
|
||||
}
|
||||
|
||||
if (canHandleJSExceptions) {
|
||||
if (canHandleJSExceptions && !codeGen.translator.options.standalone) {
|
||||
b.catch_legacy(codeGen.translator.getJsExceptionTag(b.moduleBuilder));
|
||||
|
||||
final jsExceptionLocal = codeGen.addLocal(
|
||||
|
||||
@@ -43,6 +43,8 @@ class ExceptionTags {
|
||||
}
|
||||
|
||||
w.Tag _importJsExceptionTag() {
|
||||
assert(!translator.options.standalone);
|
||||
|
||||
final w.FunctionType tagType = translator.typesBuilder.defineFunction(
|
||||
const [w.RefType.extern(nullable: true)],
|
||||
const [],
|
||||
|
||||
@@ -159,8 +159,6 @@ class WasmTarget extends Target {
|
||||
'dart:_compact_hash',
|
||||
'dart:_http',
|
||||
'dart:_internal',
|
||||
'dart:_js_helper',
|
||||
'dart:_js_types',
|
||||
'dart:_list',
|
||||
'dart:_string',
|
||||
'dart:_wasm',
|
||||
@@ -168,11 +166,16 @@ class WasmTarget extends Target {
|
||||
'dart:developer',
|
||||
'dart:ffi',
|
||||
'dart:io',
|
||||
'dart:js_interop',
|
||||
'dart:js_interop_unsafe',
|
||||
'dart:nativewrappers',
|
||||
'dart:typed_data',
|
||||
if (mode == .standalone) 'dart:_embedder',
|
||||
if (mode == .standalone)
|
||||
'dart:_embedder'
|
||||
else ...[
|
||||
'dart:_js_helper',
|
||||
'dart:_js_types',
|
||||
'dart:js_interop',
|
||||
'dart:js_interop_unsafe',
|
||||
],
|
||||
];
|
||||
|
||||
@override
|
||||
@@ -182,16 +185,19 @@ class WasmTarget extends Target {
|
||||
'dart:_boxed_int',
|
||||
'dart:_compact_hash',
|
||||
'dart:_error_utils',
|
||||
'dart:_js_helper',
|
||||
'dart:_js_types',
|
||||
'dart:_list',
|
||||
'dart:_string',
|
||||
'dart:_wasm',
|
||||
'dart:collection',
|
||||
'dart:js_interop',
|
||||
'dart:js_interop_unsafe',
|
||||
'dart:typed_data',
|
||||
if (mode == .standalone) 'dart:_embedder',
|
||||
if (mode == .standalone)
|
||||
'dart:_embedder'
|
||||
else ...[
|
||||
'dart:_js_helper',
|
||||
'dart:_js_types',
|
||||
'dart:js_interop',
|
||||
'dart:js_interop_unsafe',
|
||||
],
|
||||
];
|
||||
|
||||
@override
|
||||
@@ -319,32 +325,34 @@ class WasmTarget extends Target {
|
||||
}
|
||||
}
|
||||
|
||||
Set<Library> transitiveImportingJSInterop = {
|
||||
...jsInteropHelper.calculateTransitiveImportsOfJsInteropIfUsed(
|
||||
component.libraries,
|
||||
Uri.parse("dart:js_interop"),
|
||||
),
|
||||
...jsInteropHelper.calculateTransitiveImportsOfJsInteropIfUsed(
|
||||
component.libraries,
|
||||
Uri.parse("dart:convert"),
|
||||
),
|
||||
...jsInteropHelper.calculateTransitiveImportsOfJsInteropIfUsed(
|
||||
component.libraries,
|
||||
Uri.parse("dart:_string"),
|
||||
),
|
||||
};
|
||||
if (transitiveImportingJSInterop.isEmpty) {
|
||||
logger?.call("Skipped JS interop transformations");
|
||||
} else {
|
||||
_performJSInteropTransformations(
|
||||
component,
|
||||
coreTypes,
|
||||
hierarchy,
|
||||
transitiveImportingJSInterop,
|
||||
diagnosticReporter,
|
||||
referenceFromIndex,
|
||||
);
|
||||
logger?.call("Transformed JS interop classes");
|
||||
if (mode != .standalone) {
|
||||
Set<Library> transitiveImportingJSInterop = {
|
||||
...jsInteropHelper.calculateTransitiveImportsOfJsInteropIfUsed(
|
||||
component.libraries,
|
||||
Uri.parse("dart:js_interop"),
|
||||
),
|
||||
...jsInteropHelper.calculateTransitiveImportsOfJsInteropIfUsed(
|
||||
component.libraries,
|
||||
Uri.parse("dart:convert"),
|
||||
),
|
||||
...jsInteropHelper.calculateTransitiveImportsOfJsInteropIfUsed(
|
||||
component.libraries,
|
||||
Uri.parse("dart:_string"),
|
||||
),
|
||||
};
|
||||
if (transitiveImportingJSInterop.isEmpty) {
|
||||
logger?.call("Skipped JS interop transformations");
|
||||
} else {
|
||||
_performJSInteropTransformations(
|
||||
component,
|
||||
coreTypes,
|
||||
hierarchy,
|
||||
transitiveImportingJSInterop,
|
||||
diagnosticReporter,
|
||||
referenceFromIndex,
|
||||
);
|
||||
logger?.call("Transformed JS interop classes");
|
||||
}
|
||||
}
|
||||
|
||||
// If we are compiling with a null environment, skip constant resolution
|
||||
|
||||
+2
-2
@@ -6,8 +6,8 @@ Part 0
|
||||
- pkg/compiler/test/custom_split/data/fuse_with_and/lib4.dart prefix: b4
|
||||
References
|
||||
- dart:_boxed_int::BoxedInt::@methods::toRadixString
|
||||
- dart:_string::@methods::dart:_string::_JS_Inline_22
|
||||
- dart:_string::@methods::dart:_string::_JS_Inline_30
|
||||
- dart:_string::@methods::dart:_string::_JS_Inline_173
|
||||
- dart:_string::@methods::dart:_string::_JS_Inline_181
|
||||
- dart:_string::@methods::dart:_string::_jsIdentical
|
||||
- dart:_string::@methods::dart:_string::_jsStringToUpperCase
|
||||
- dart:_string::@methods::jsStringFromCharCodeArray
|
||||
|
||||
+2
-2
@@ -6,8 +6,8 @@ Part 0
|
||||
- pkg/compiler/test/custom_split/data/fuse_with_and/lib4.dart prefix: b4
|
||||
References
|
||||
- dart:_boxed_int::BoxedInt::@methods::toRadixString
|
||||
- dart:_string::@methods::dart:_string::_JS_Inline_22
|
||||
- dart:_string::@methods::dart:_string::_JS_Inline_30
|
||||
- dart:_string::@methods::dart:_string::_JS_Inline_173
|
||||
- dart:_string::@methods::dart:_string::_JS_Inline_181
|
||||
- dart:_string::@methods::dart:_string::_jsIdentical
|
||||
- dart:_string::@methods::dart:_string::_jsStringToUpperCase
|
||||
- dart:_string::@methods::jsStringFromCharCodeArray
|
||||
|
||||
+2
-2
@@ -6,8 +6,8 @@ Part 0
|
||||
- pkg/compiler/test/custom_split/data/fuse_with_or/lib4.dart prefix: b4
|
||||
References
|
||||
- dart:_boxed_int::BoxedInt::@methods::toRadixString
|
||||
- dart:_string::@methods::dart:_string::_JS_Inline_22
|
||||
- dart:_string::@methods::dart:_string::_JS_Inline_30
|
||||
- dart:_string::@methods::dart:_string::_JS_Inline_173
|
||||
- dart:_string::@methods::dart:_string::_JS_Inline_181
|
||||
- dart:_string::@methods::dart:_string::_jsIdentical
|
||||
- dart:_string::@methods::dart:_string::_jsStringToUpperCase
|
||||
- dart:_string::@methods::jsStringFromCharCodeArray
|
||||
|
||||
+2
-2
@@ -6,8 +6,8 @@ Part 0
|
||||
- pkg/compiler/test/custom_split/data/fuse_with_or/lib4.dart prefix: b4
|
||||
References
|
||||
- dart:_boxed_int::BoxedInt::@methods::toRadixString
|
||||
- dart:_string::@methods::dart:_string::_JS_Inline_22
|
||||
- dart:_string::@methods::dart:_string::_JS_Inline_30
|
||||
- dart:_string::@methods::dart:_string::_JS_Inline_173
|
||||
- dart:_string::@methods::dart:_string::_JS_Inline_181
|
||||
- dart:_string::@methods::dart:_string::_jsIdentical
|
||||
- dart:_string::@methods::dart:_string::_jsStringToUpperCase
|
||||
- dart:_string::@methods::jsStringFromCharCodeArray
|
||||
|
||||
+2
-2
@@ -6,8 +6,8 @@ Part 0
|
||||
- pkg/compiler/test/custom_split/data/just_fuse/lib4.dart prefix: b4
|
||||
References
|
||||
- dart:_boxed_int::BoxedInt::@methods::toRadixString
|
||||
- dart:_string::@methods::dart:_string::_JS_Inline_22
|
||||
- dart:_string::@methods::dart:_string::_JS_Inline_30
|
||||
- dart:_string::@methods::dart:_string::_JS_Inline_173
|
||||
- dart:_string::@methods::dart:_string::_JS_Inline_181
|
||||
- dart:_string::@methods::dart:_string::_jsIdentical
|
||||
- dart:_string::@methods::dart:_string::_jsStringToUpperCase
|
||||
- dart:_string::@methods::jsStringFromCharCodeArray
|
||||
|
||||
+2
-2
@@ -6,8 +6,8 @@ Part 0
|
||||
- pkg/compiler/test/custom_split/data/just_fuse/lib4.dart prefix: b4
|
||||
References
|
||||
- dart:_boxed_int::BoxedInt::@methods::toRadixString
|
||||
- dart:_string::@methods::dart:_string::_JS_Inline_22
|
||||
- dart:_string::@methods::dart:_string::_JS_Inline_30
|
||||
- dart:_string::@methods::dart:_string::_JS_Inline_173
|
||||
- dart:_string::@methods::dart:_string::_JS_Inline_181
|
||||
- dart:_string::@methods::dart:_string::_jsIdentical
|
||||
- dart:_string::@methods::dart:_string::_jsStringToUpperCase
|
||||
- dart:_string::@methods::jsStringFromCharCodeArray
|
||||
|
||||
@@ -116,7 +116,7 @@
|
||||
local.get $var0
|
||||
local.get $onlyUsedInSuper
|
||||
local.set $var3
|
||||
i32.const 63
|
||||
i32.const 85
|
||||
local.get $var3
|
||||
struct.new $BoxedInt
|
||||
call $"new _MixinApplication1&Base&SubMixin.named (initializer)"
|
||||
@@ -248,7 +248,7 @@
|
||||
global.get $"\", \""
|
||||
local.get $onlyUsedInSubBody
|
||||
local.set $var4
|
||||
i32.const 63
|
||||
i32.const 85
|
||||
local.get $var4
|
||||
struct.new $BoxedInt
|
||||
array.new_fixed $Array<Object?> 6
|
||||
@@ -273,7 +273,7 @@
|
||||
local.get $var0
|
||||
local.get $onlyUsedInSuper1
|
||||
local.set $var3
|
||||
i32.const 63
|
||||
i32.const 85
|
||||
local.get $var3
|
||||
struct.new $BoxedInt
|
||||
local.get $onlyUsedInSuper2
|
||||
@@ -328,7 +328,7 @@
|
||||
local.get $var0
|
||||
local.get $onlyUsedInSuper1
|
||||
local.set $var3
|
||||
i32.const 63
|
||||
i32.const 85
|
||||
local.get $var3
|
||||
struct.new $BoxedInt
|
||||
call $"new _MixinApplication1&Base&SubMixin.sub2 (initializer)"
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
(global $MyConstClass (ref $MyConstClass)
|
||||
(i32.const 108)
|
||||
(i32.const 0)
|
||||
(i32.const 103)
|
||||
(i32.const 60)
|
||||
(i32.const 0)
|
||||
(global.get $.h1-nonshared-const)
|
||||
(struct.new $JSExternWrapper)
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
(global $MyConstClass (ref $MyConstClass)
|
||||
(i32.const 108)
|
||||
(i32.const 0)
|
||||
(i32.const 103)
|
||||
(i32.const 60)
|
||||
(i32.const 0)
|
||||
(global.get $.h0-nonshared-const)
|
||||
(struct.new $JSExternWrapper)
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
(global $MyConstClass (ref $MyConstClass)
|
||||
(i32.const 108)
|
||||
(i32.const 0)
|
||||
(i32.const 103)
|
||||
(i32.const 60)
|
||||
(i32.const 0)
|
||||
(global.get $.shared-const)
|
||||
(struct.new $JSExternWrapper)
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
(global $".Foo called " (import "" "Foo called ") (ref extern))
|
||||
(table $module0.cross-module-funcs-0 (import "module0" "cross-module-funcs-0") 4 funcref)
|
||||
(global $"\"Foo called \"" (ref $JSExternWrapper)
|
||||
(i32.const 102)
|
||||
(i32.const 58)
|
||||
(i32.const 0)
|
||||
(global.get $".Foo called ")
|
||||
(struct.new $JSExternWrapper))
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
(ref.null none))
|
||||
(global $"\">(\"" (ref $JSExternWrapper) <...>)
|
||||
(global $"\"globalH1Bar<\"" (ref $JSExternWrapper)
|
||||
(i32.const 102)
|
||||
(i32.const 58)
|
||||
(i32.const 0)
|
||||
(global.get $.globalH1Bar<)
|
||||
(struct.new $JSExternWrapper))
|
||||
@@ -144,7 +144,7 @@
|
||||
struct.get $H1 $fun
|
||||
local.tee $var0
|
||||
struct.get $#Closure-0-1 $context
|
||||
i32.const 64
|
||||
i32.const 86
|
||||
i64.const 1
|
||||
struct.new $BoxedInt
|
||||
local.get $var0
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
(table $module0.constant-table0 (import "module0" "constant-table0") 1 (ref null $_FunctionType))
|
||||
(table $module0.cross-module-funcs-0 (import "module0" "cross-module-funcs-0") 11 funcref)
|
||||
(global $"\"globalH0Foo\"" (ref $JSExternWrapper)
|
||||
(i32.const 102)
|
||||
(i32.const 58)
|
||||
(i32.const 0)
|
||||
(global.get $.globalH0Foo)
|
||||
(struct.new $JSExternWrapper))
|
||||
|
||||
@@ -11,24 +11,24 @@
|
||||
(global $1 (import "module0" "global2") (ref $BoxedInt))
|
||||
(global $2 (import "module0" "global3") (ref $BoxedInt))
|
||||
(table $module0.cross-module-funcs-0 (import "module0" "cross-module-funcs-0") 18 funcref)
|
||||
(table $module0.dispatch0 (import "module0" "dispatch0") 649 funcref)
|
||||
(table $module0.dispatch0 (import "module0" "dispatch0") 654 funcref)
|
||||
(global $"\"Foo0.doitDispatch(\"" (ref $JSExternWrapper)
|
||||
(i32.const 103)
|
||||
(i32.const 60)
|
||||
(i32.const 0)
|
||||
(global.get $".Foo0.doitDispatch(")
|
||||
(struct.new $JSExternWrapper))
|
||||
(global $"\"Foo1.doitDevirt(\"" (ref $JSExternWrapper)
|
||||
(i32.const 103)
|
||||
(i32.const 60)
|
||||
(i32.const 0)
|
||||
(global.get $".Foo1.doitDevirt(")
|
||||
(struct.new $JSExternWrapper))
|
||||
(global $"\"Foo1.doitDispatch(\"" (ref $JSExternWrapper)
|
||||
(i32.const 103)
|
||||
(i32.const 60)
|
||||
(i32.const 0)
|
||||
(global.get $".Foo1.doitDispatch(")
|
||||
(struct.new $JSExternWrapper))
|
||||
(global $"\"FooBase(\"" (ref $JSExternWrapper)
|
||||
(i32.const 103)
|
||||
(i32.const 60)
|
||||
(i32.const 0)
|
||||
(global.get $".FooBase(")
|
||||
(struct.new $JSExternWrapper))
|
||||
@@ -71,7 +71,7 @@
|
||||
global.get $1
|
||||
local.get $var0
|
||||
struct.get $Object $field0
|
||||
i32.const 457
|
||||
i32.const 365
|
||||
i32.add
|
||||
call_indirect $module0.dispatch0 (param (ref $Object) (ref null $#Top))
|
||||
block $label2 (result (ref $Object))
|
||||
|
||||
@@ -22,44 +22,44 @@
|
||||
(global $FooConst0 (import "module0" "global7") (ref $Object))
|
||||
(global $fooGlobal0 (import "module0" "global16") (ref null $#Top))
|
||||
(table $module0.cross-module-funcs-0 (import "module0" "cross-module-funcs-0") 45 funcref)
|
||||
(table $module0.dispatch0 (import "module0" "dispatch0") 666 funcref)
|
||||
(table $module0.dispatch0 (import "module0" "dispatch0") 670 funcref)
|
||||
(global $"\"0\"" (ref $JSExternWrapper) <...>)
|
||||
(global $"\"1\"" (ref $JSExternWrapper) <...>)
|
||||
(global $"\"2\"" (ref $JSExternWrapper) <...>)
|
||||
(global $"\"3\"" (ref $JSExternWrapper) <...>)
|
||||
(global $"\"4\"" (ref $JSExternWrapper) <...>)
|
||||
(global $"\"FooConst0(\"" (ref $JSExternWrapper)
|
||||
(i32.const 103)
|
||||
(i32.const 60)
|
||||
(i32.const 0)
|
||||
(global.get $".FooConst0(")
|
||||
(struct.new $JSExternWrapper))
|
||||
(global $"\"FooConst1(\"" (ref $JSExternWrapper)
|
||||
(i32.const 103)
|
||||
(i32.const 60)
|
||||
(i32.const 0)
|
||||
(global.get $".FooConst1(")
|
||||
(struct.new $JSExternWrapper))
|
||||
(global $"\"FooConst2(\"" (ref $JSExternWrapper)
|
||||
(i32.const 103)
|
||||
(i32.const 60)
|
||||
(i32.const 0)
|
||||
(global.get $".FooConst2(")
|
||||
(struct.new $JSExternWrapper))
|
||||
(global $"\"FooConst3(\"" (ref $JSExternWrapper)
|
||||
(i32.const 103)
|
||||
(i32.const 60)
|
||||
(i32.const 0)
|
||||
(global.get $".FooConst3(")
|
||||
(struct.new $JSExternWrapper))
|
||||
(global $"\"FooConst4(\"" (ref $JSExternWrapper)
|
||||
(i32.const 103)
|
||||
(i32.const 60)
|
||||
(i32.const 0)
|
||||
(global.get $".FooConst4(")
|
||||
(struct.new $JSExternWrapper))
|
||||
(global $"\"FooConst5(\"" (ref $JSExternWrapper)
|
||||
(i32.const 103)
|
||||
(i32.const 60)
|
||||
(i32.const 0)
|
||||
(global.get $".FooConst5(")
|
||||
(struct.new $JSExternWrapper))
|
||||
(global $"\"FooConstBase(\"" (ref $JSExternWrapper)
|
||||
(i32.const 103)
|
||||
(i32.const 60)
|
||||
(i32.const 0)
|
||||
(global.get $".FooConstBase(")
|
||||
(struct.new $JSExternWrapper))
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
(type $_Type <...>)
|
||||
(global $"\")\"_11" (import "$" "2") (ref $JSExternWrapper))
|
||||
(global $_InterfaceType (import "$" "0") (ref $_InterfaceType))
|
||||
(table $$.% (import "$" "%") 742 funcref)
|
||||
(table $$.% (import "$" "%") 765 funcref)
|
||||
(table $$.' (import "$" "'") 20 funcref)
|
||||
(global $"\">.takeT(\"" (ref $JSExternWrapper) <...>)
|
||||
(global $"\"Foo<\"" (ref $JSExternWrapper) <...>)
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
(type $WasmListBase <...>)
|
||||
(type $_Type <...>)
|
||||
(table $module0.cross-module-funcs-0 (import "module0" "cross-module-funcs-0") 12 funcref)
|
||||
(table $module0.dispatch0 (import "module0" "dispatch0") 666 funcref)
|
||||
(table $module0.dispatch0 (import "module0" "dispatch0") 670 funcref)
|
||||
(elem $module0.cross-module-funcs-0
|
||||
(set 0 (ref.func $"runTest <noInline>")))
|
||||
(func $"runTest <noInline>"
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
(table $dtable0 3 i31ref)
|
||||
(table $dtable2 3 funcref)
|
||||
(global $1 (ref $BoxedInt)
|
||||
(i32.const 63)
|
||||
(i32.const 85)
|
||||
(i64.const 1)
|
||||
(struct.new $BoxedInt))
|
||||
(global $true (ref $BoxedBool)
|
||||
|
||||
@@ -5,13 +5,13 @@
|
||||
(field $field0 i32)
|
||||
(field $value i64))))
|
||||
(global $1 (ref $BoxedInt)
|
||||
(i32.const 59)
|
||||
(i32.const 84)
|
||||
(i64.const 1)
|
||||
(struct.new $BoxedInt))
|
||||
(func $"main <noInline>"
|
||||
global.get $1
|
||||
call $print
|
||||
i32.const 59
|
||||
i32.const 84
|
||||
i64.const 2
|
||||
struct.new $BoxedInt
|
||||
call $print
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
(field $_externRef externref))))
|
||||
(global $".hello world" (import "" "hello world") (ref extern))
|
||||
(global $"\"hello world\"" (ref $JSExternWrapper)
|
||||
(i32.const 95)
|
||||
(i32.const 56)
|
||||
(global.get $".hello world")
|
||||
(struct.new $JSExternWrapper))
|
||||
(func $"main <noInline>"
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
(global $".hello world" (import "" "hello world") (ref extern))
|
||||
(table $module0.cross-module-funcs-0 (import "module0" "cross-module-funcs-0") 2 funcref)
|
||||
(global $"\"hello world\"" (ref $JSExternWrapper)
|
||||
(i32.const 96)
|
||||
(i32.const 57)
|
||||
(i32.const 0)
|
||||
(global.get $".hello world")
|
||||
(struct.new $JSExternWrapper))
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
else
|
||||
call $"ktrue implicit getter"
|
||||
if (result (ref null $BoxedDouble))
|
||||
i32.const 72
|
||||
i32.const 92
|
||||
call $"doubleValue implicit getter"
|
||||
struct.new $BoxedDouble
|
||||
else
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
else
|
||||
call $"ktrue implicit getter"
|
||||
if (result (ref null $BoxedInt))
|
||||
i32.const 62
|
||||
i32.const 86
|
||||
call $"intValue implicit getter"
|
||||
struct.new $BoxedInt
|
||||
else
|
||||
|
||||
@@ -9,30 +9,30 @@
|
||||
i32.const 1
|
||||
memory.grow $foo.mem
|
||||
drop
|
||||
i32.const 67
|
||||
i32.const 89
|
||||
i32.const 0
|
||||
f32.load align=4
|
||||
f64.promote_f32
|
||||
struct.new $BoxedDouble
|
||||
call $print
|
||||
i32.const 67
|
||||
i32.const 89
|
||||
i32.const 0
|
||||
f32.load align=4
|
||||
f64.promote_f32
|
||||
struct.new $BoxedDouble
|
||||
call $print
|
||||
i32.const 67
|
||||
i32.const 89
|
||||
i32.const 0
|
||||
f64.load align=8
|
||||
struct.new $BoxedDouble
|
||||
call $print
|
||||
i32.const 67
|
||||
i32.const 89
|
||||
i32.const 1
|
||||
f32.load align=4
|
||||
f64.promote_f32
|
||||
struct.new $BoxedDouble
|
||||
call $print
|
||||
i32.const 67
|
||||
i32.const 89
|
||||
i32.const 1
|
||||
f32.load align=4
|
||||
f64.promote_f32
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
global.get $"\")\""
|
||||
call $JSStringImpl._interpolate3
|
||||
drop
|
||||
i32.const 59
|
||||
i32.const 84
|
||||
local.get $var0
|
||||
struct.get $JSExternWrapper $_externRef
|
||||
call $"wasm:js-string.length (import)"
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
(module $module0
|
||||
(type $#Top (struct
|
||||
(field $field0 i32)))
|
||||
(type $JSExternWrapper (sub $#Top (struct
|
||||
(type $JSStringImpl (sub final $#Top (struct
|
||||
(field $field0 i32)
|
||||
(field $_externRef externref))))
|
||||
(global $"\"Hello world\"" (mut (ref null $JSExternWrapper))
|
||||
(field $_ref (ref extern)))))
|
||||
(global $"\"Hello world\"" (mut (ref null $JSStringImpl))
|
||||
(ref.null none))
|
||||
)
|
||||
@@ -5,9 +5,9 @@
|
||||
(type $JSExternWrapper <...>)
|
||||
(type $JavaScriptStack <...>)
|
||||
(tag $tag0 (param (ref $#Top) (ref $#Top)))
|
||||
(global $"WasmArray<WasmI16>[748]" (ref $Array<WasmI16>) <...>)
|
||||
(global $"WasmArray<WasmI16>[688]" (ref $Array<WasmI16>) <...>)
|
||||
(global $"WasmArray<WasmI32>[225]" (ref $Array<WasmI32>) <...>)
|
||||
(global $"WasmArray<WasmI32>[748]" (ref $Array<WasmI32>) <...>)
|
||||
(global $"WasmArray<WasmI32>[688]" (ref $Array<WasmI32>) <...>)
|
||||
(global $"\"Caught Error\"" (ref $JSExternWrapper) <...>)
|
||||
(global $"\"Caught JSAny\"" (ref $JSExternWrapper) <...>)
|
||||
(global $"\"Caught Object\"" (ref $JSExternWrapper) <...>)
|
||||
@@ -32,7 +32,7 @@
|
||||
local.get $var1
|
||||
struct.get $#Top $field0
|
||||
local.tee $var0
|
||||
i32.const 76
|
||||
i32.const 59
|
||||
i32.eq
|
||||
if (result i32)
|
||||
i32.const 0
|
||||
@@ -40,22 +40,22 @@
|
||||
block $label3 (result i32)
|
||||
i32.const -1
|
||||
global.get $"WasmArray<WasmI32>[225]"
|
||||
i32.const 76
|
||||
i32.const 59
|
||||
array.get $Array<WasmI32>
|
||||
local.get $var0
|
||||
i32.add
|
||||
local.tee $var0
|
||||
i32.const 748
|
||||
i32.const 688
|
||||
i32.ge_u
|
||||
br_if $label3
|
||||
drop
|
||||
global.get $"WasmArray<WasmI32>[748]"
|
||||
global.get $"WasmArray<WasmI32>[688]"
|
||||
local.get $var0
|
||||
array.get $Array<WasmI32>
|
||||
i32.const 76
|
||||
i32.const 59
|
||||
i32.eq
|
||||
if
|
||||
global.get $"WasmArray<WasmI16>[748]"
|
||||
global.get $"WasmArray<WasmI16>[688]"
|
||||
local.get $var0
|
||||
array.get_u $Array<WasmI16>
|
||||
br $label3
|
||||
@@ -128,30 +128,52 @@
|
||||
local.set $var2
|
||||
local.set $var1
|
||||
block $label3 (result i32)
|
||||
i32.const 1
|
||||
local.get $var1
|
||||
struct.get $#Top $field0
|
||||
local.tee $var0
|
||||
i32.const 40
|
||||
i32.eq
|
||||
br_if $label3
|
||||
drop
|
||||
i32.const 1
|
||||
local.get $var0
|
||||
i32.const 43
|
||||
i32.sub
|
||||
i32.const 12
|
||||
i32.lt_u
|
||||
br_if $label3
|
||||
drop
|
||||
i32.const 1
|
||||
local.get $var0
|
||||
i32.const 99
|
||||
i32.sub
|
||||
i32.const 2
|
||||
i32.lt_u
|
||||
br_if $label3
|
||||
drop
|
||||
block $label4
|
||||
local.get $var1
|
||||
struct.get $#Top $field0
|
||||
local.tee $var0
|
||||
i32.const 54
|
||||
i32.le_u
|
||||
if
|
||||
local.get $var0
|
||||
i32.const 40
|
||||
i32.le_u
|
||||
if
|
||||
i32.const 1
|
||||
local.get $var0
|
||||
i32.const 40
|
||||
i32.eq
|
||||
br_if $label3
|
||||
drop
|
||||
br $label4
|
||||
end
|
||||
i32.const 1
|
||||
local.get $var0
|
||||
i32.const 43
|
||||
i32.ge_u
|
||||
br_if $label3
|
||||
drop
|
||||
br $label4
|
||||
end
|
||||
local.get $var0
|
||||
i32.const 101
|
||||
i32.le_u
|
||||
if
|
||||
i32.const 1
|
||||
local.get $var0
|
||||
i32.const 101
|
||||
i32.eq
|
||||
br_if $label3
|
||||
drop
|
||||
br $label4
|
||||
end
|
||||
i32.const 1
|
||||
local.get $var0
|
||||
i32.const 105
|
||||
i32.eq
|
||||
br_if $label3
|
||||
drop
|
||||
end $label4
|
||||
i32.const 0
|
||||
end $label3
|
||||
br_if $label1
|
||||
|
||||
@@ -343,6 +343,9 @@ and `base64 -w 0`:
|
||||
(import "wasm:js-string" "fromCharCodeArray"
|
||||
(func $fromCharCodeArray (param (ref null $i16array) i32 i32) (result (ref extern)))
|
||||
)
|
||||
(import "wasm:js-string" "intoCharCodeArray"
|
||||
(func $intoCharCodeArray (param (ref null extern) (ref null $i16array) i32) (result i32))
|
||||
)
|
||||
|
||||
(func (export "stringFromAsciiBytes")
|
||||
(param $arr (ref $i8array))
|
||||
@@ -357,21 +360,24 @@ and `base64 -w 0`:
|
||||
(local.set $i (local.get $length))
|
||||
(local.set $expanded (array.new $i16array (i32.const 0) (local.get $length)))
|
||||
|
||||
;; do { i--; expanded[i] = arr[i]; } while (i >= start);
|
||||
;; do { i--; expanded[i] = arr[i + start]; } while (i >= 0);
|
||||
(block $break
|
||||
(loop $loop
|
||||
(local.set $i (i32.add (local.get $i) (i32.const -1)))
|
||||
(br_if $break
|
||||
(i32.lt_s
|
||||
(local.get $i)
|
||||
(local.get $start)
|
||||
(i32.const 0)
|
||||
)
|
||||
)
|
||||
|
||||
(array.set $i16array
|
||||
(local.get $expanded)
|
||||
(local.get $i)
|
||||
(array.get_u $i8array (local.get $arr) (local.get $i))
|
||||
(array.get_u $i8array
|
||||
(local.get $arr)
|
||||
(i32.add (local.get $i) (local.get $start))
|
||||
)
|
||||
)
|
||||
br $loop
|
||||
)
|
||||
@@ -400,6 +406,18 @@ and `base64 -w 0`:
|
||||
)
|
||||
)
|
||||
|
||||
(func (export "stringToCharCodeArray")
|
||||
(param $str externref)
|
||||
(param $array (ref $i16array))
|
||||
(param $start i32)
|
||||
|
||||
(drop (call $intoCharCodeArray
|
||||
(local.get $str)
|
||||
(local.get $array)
|
||||
(local.get $start)
|
||||
))
|
||||
)
|
||||
|
||||
(func (export "emptyExternRefArray")
|
||||
(result (ref $externarray))
|
||||
(array.new_default $externarray (i32.const 0))
|
||||
@@ -407,15 +425,13 @@ and `base64 -w 0`:
|
||||
)
|
||||
*/
|
||||
const _wasmStandaloneArrayHelper =
|
||||
'AGFzbQEAAAABKgdedwFeeAFebwFgA2MAf38BZG9gA2QBf38BZG9gA2QAf38BZG9gAAFkAgIkAQ53YXNtOmpzLXN0cmluZxFmcm9tQ2hhckNvZGVBcnJheQADAwQDBAUGB0gDFHN0cmluZ0Zyb21Bc2NpaUJ5dGVzAAEXc3RyaW5nRnJvbUNoYXJDb2RlQXJyYXkAAhNlbXB0eUV4dGVyblJlZkFycmF5AAMKWwNDAgF/AWQAIAIhA0EAIAL7BgAhBAJAA0AgA0F/aiEDIAMgAUgNASAEIAMgACAD+w0B+w4ADAALAAsgBEEAIAT7DxAACw0AIAAgASABIAJqEAALBwBBAPsHAgs=';
|
||||
'AGFzbQEAAAABOQledwFeeAFebwFgA2MAf38BZG9gA29jAH8Bf2ADZAF/fwFkb2ADZAB/fwFkb2ADb2QAfwBgAAFkAgJHAg53YXNtOmpzLXN0cmluZxFmcm9tQ2hhckNvZGVBcnJheQADDndhc206anMtc3RyaW5nEWludG9DaGFyQ29kZUFycmF5AAQDBQQFBgcIB2AEFHN0cmluZ0Zyb21Bc2NpaUJ5dGVzAAIXc3RyaW5nRnJvbUNoYXJDb2RlQXJyYXkAAxVzdHJpbmdUb0NoYXJDb2RlQXJyYXkABBNlbXB0eUV4dGVyblJlZkFycmF5AAUKagRGAgF/AWQAIAIhA0EAIAL7BgAhBAJAA0AgA0F/aiEDIANBAEgNASAEIAMgACADIAFq+w0B+w4ADAALAAsgBEEAIAT7DxAACw0AIAAgASABIAJqEAALCwAgACABIAIQARoLBwBBAPsHAgs=';
|
||||
|
||||
String dart2wasmHtml(
|
||||
String title,
|
||||
String wasmPath,
|
||||
String mjsPath,
|
||||
String supportJsPath,
|
||||
bool standalone,
|
||||
) {
|
||||
/*
|
||||
|
||||
*/
|
||||
|
||||
String dart2WasmStandaloneHtml(String title, String wasmPath) {
|
||||
const standaloneEmbedder =
|
||||
"""
|
||||
const { instance: helperInstance } = await WebAssembly.instantiate(Uint8Array.fromBase64('$_wasmStandaloneArrayHelper'), {}, {
|
||||
@@ -447,6 +463,27 @@ String dart2wasmHtml(
|
||||
const str = helperInstance.exports.stringFromCharCodeArray(chars, start, length);
|
||||
return str;
|
||||
},
|
||||
stringLength: (s) => s.length,
|
||||
stringEquals: (a, b) => a === b,
|
||||
stringCompare: (a, b) => a === b ? 0 : (a < b ? -1 : 1),
|
||||
stringCodeUnitAt: (str, idx) => str.charCodeAt(idx),
|
||||
stringIndexOfString: (a, b, start) => a.indexOf(b, start),
|
||||
stringLastIndexOfString: (a, b, start) => a.lastIndexOf(b, start),
|
||||
stringReplaceAllString: (str, needle, replace) => str.replaceAll(needle, () => replace),
|
||||
stringReplaceAllRegExp: (str, regex, replace) => str.replaceAll(regex.regular, () => replace),
|
||||
stringSubstring: (str, start, end) => str.substring(start, end),
|
||||
stringToLowerCase: (str) => str.toLowerCase(),
|
||||
stringToUpperCase: (str) => str.toUpperCase(),
|
||||
stringConcat: (a, b) => a + b,
|
||||
stringRepeat: (str, amount) => str.repeat(amount),
|
||||
stringReplaceRange: (str, start, end, replacement) => {
|
||||
const before = str.substring(0, start);
|
||||
const after = str.substring(end);
|
||||
return before + replacement + after;
|
||||
},
|
||||
stringToCodeUnits: (str, array, startIndex) => {
|
||||
helperInstance.exports.stringToCharCodeArray(str, array, startIndex);
|
||||
},
|
||||
monotonicClockFrequency: () => 1_000_000,
|
||||
monotonicClockTicks: () => BigInt(Math.round(performance.now() * 1000)),
|
||||
weakRefCreate: (dartValue) => new WeakRef(dartValue),
|
||||
@@ -627,14 +664,61 @@ String dart2wasmHtml(
|
||||
reportTaskEvent: (taskId, flowId, type, name, jsonArgs) => {},
|
||||
};
|
||||
""";
|
||||
final additionalImports = standalone ? '{ dart: dartEmbedder }' : '{}';
|
||||
final mainInvocation = standalone
|
||||
? r'appInstance.instantiatedModule.exports.$invokeMain(helperInstance.exports.emptyExternRefArray())'
|
||||
: 'appInstance.invokeMain();';
|
||||
|
||||
return """
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="dart.unittest" content="full-stack-traces">
|
||||
<title> Test $title </title>
|
||||
<link rel="preload" href="$wasmPath" as="fetch" crossorigin>
|
||||
<style>
|
||||
.unittest-table { font-family:monospace; border:1px; }
|
||||
.unittest-pass { background: #6b3;}
|
||||
.unittest-fail { background: #d55;}
|
||||
.unittest-error { background: #a11;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1> Running $title</h1>
|
||||
<script type="text/javascript"
|
||||
src="/root_dart/pkg/test_runner/lib/src/test_controller.js">
|
||||
</script>
|
||||
<script type="module">
|
||||
$standaloneEmbedder
|
||||
|
||||
// Default stack trace limit in V8 is 10, which hides some of the stack frames
|
||||
// we check in stack trace tests.
|
||||
Error.stackTraceLimit = 20;
|
||||
async function loadAndRun(wasmPath) {
|
||||
const { instance } = await WebAssembly.instantiateStreaming(fetch(wasmPath), {dart: dartEmbedder});
|
||||
window.loadData = async (relativeToWasmFileUri) => {
|
||||
const path = '$wasmPath'.slice(0, wasmPath.lastIndexOf('/'));
|
||||
const response = await fetch(`\${path}/\${relativeToWasmFileUri}`);
|
||||
return response.arrayBuffer();
|
||||
};
|
||||
|
||||
dartMainRunner(() => {
|
||||
instance.exports.\$invokeMain(helperInstance.exports.emptyExternRefArray());
|
||||
});
|
||||
}
|
||||
|
||||
loadAndRun('$wasmPath');
|
||||
</script>
|
||||
</body>
|
||||
</html>""";
|
||||
}
|
||||
|
||||
String dart2wasmHtml(
|
||||
String title,
|
||||
String wasmPath,
|
||||
String mjsPath,
|
||||
String supportJsPath,
|
||||
) {
|
||||
return """
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta charset="utf-8">
|
||||
@@ -674,13 +758,12 @@ String dart2wasmHtml(
|
||||
const response = await fetch(`\${path}/\${relativeToWasmFileUri}`);
|
||||
return response.arrayBuffer();
|
||||
};
|
||||
${standalone ? standaloneEmbedder : ''}
|
||||
const appInstance = await compiledApp.instantiate($additionalImports, {
|
||||
const appInstance = await compiledApp.instantiate({}, {
|
||||
loadDeferredModules: (modules, handleWasmBytes) =>
|
||||
Promise.all(modules.map((m) => fetch(m).then((b) => handleWasmBytes(m, b)))),
|
||||
});
|
||||
dartMainRunner(() => {
|
||||
$mainInvocation
|
||||
appInstance.invokeMain();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1078,13 +1078,15 @@ class StandardTestSuite extends TestSuite {
|
||||
Path('$outputDir/$nameNoExt.support.js'),
|
||||
);
|
||||
|
||||
content = dart2wasmHtml(
|
||||
testFile.path.toNativePath(),
|
||||
wasmPath,
|
||||
mjsPath,
|
||||
supportJsPath,
|
||||
configuration.isDart2wasmStandalone,
|
||||
);
|
||||
final title = testFile.path.toNativePath();
|
||||
content = configuration.isDart2wasmStandalone
|
||||
? dart2WasmStandaloneHtml(title, wasmPath)
|
||||
: dart2wasmHtml(
|
||||
testFile.path.toNativePath(),
|
||||
wasmPath,
|
||||
mjsPath,
|
||||
supportJsPath,
|
||||
);
|
||||
} else if (configuration.compiler == Compiler.ddc) {
|
||||
var ddcConfig =
|
||||
configuration.compilerConfiguration as DevCompilerConfiguration;
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
// 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 "dart:_internal" show patch;
|
||||
import "dart:_internal" show patch, unsafeCast;
|
||||
import "dart:_string" show StringUncheckedOperations;
|
||||
import "dart:_string_helper" show skipLeadingWhitespace, skipTrailingWhitespace;
|
||||
import "dart:_wasm";
|
||||
import "dart:_error_utils";
|
||||
|
||||
@@ -53,11 +54,11 @@ class int {
|
||||
int? radix,
|
||||
int? Function(String)? onError,
|
||||
) {
|
||||
int end = source.lastNonWhitespace() + 1;
|
||||
int end = skipTrailingWhitespace(source, source.length);
|
||||
if (end == 0) {
|
||||
return _handleFormatError(onError, source, source.length, radix, null);
|
||||
}
|
||||
int start = source.firstNonWhitespace();
|
||||
int start = skipLeadingWhitespace(source, 0);
|
||||
|
||||
int first = source.codeUnitAtUnchecked(start);
|
||||
int sign = 1;
|
||||
|
||||
@@ -308,61 +308,7 @@ final class JSStringImpl extends js.JSExternWrapper
|
||||
String Function(Match)? onMatch,
|
||||
String Function(String)? onNonMatch,
|
||||
}) {
|
||||
if (onMatch == null) onMatch = _matchString;
|
||||
if (onNonMatch == null) onNonMatch = _stringIdentity;
|
||||
if (from is String) {
|
||||
final patternLength = from.length;
|
||||
if (patternLength == 0) {
|
||||
// Pattern is the empty string.
|
||||
StringBuffer buffer = StringBuffer();
|
||||
int i = 0;
|
||||
buffer.write(onNonMatch(""));
|
||||
final length = this.length;
|
||||
while (i < length) {
|
||||
buffer.write(onMatch(StringMatch(i, this, "")));
|
||||
// Special case to avoid splitting a surrogate pair.
|
||||
int code = codeUnitAt(i);
|
||||
if ((code & ~0x3FF) == 0xD800 && length > i + 1) {
|
||||
// Leading surrogate;
|
||||
code = codeUnitAt(i + 1);
|
||||
if ((code & ~0x3FF) == 0xDC00) {
|
||||
// Matching trailing surrogate.
|
||||
buffer.write(onNonMatch(substring(i, i + 2)));
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
buffer.write(onNonMatch(this[i]));
|
||||
i++;
|
||||
}
|
||||
buffer.write(onMatch(StringMatch(i, this, "")));
|
||||
buffer.write(onNonMatch(""));
|
||||
return buffer.toString();
|
||||
}
|
||||
StringBuffer buffer = StringBuffer();
|
||||
int startIndex = 0;
|
||||
final length = this.length;
|
||||
while (startIndex < length) {
|
||||
int position = indexOf(from, startIndex);
|
||||
if (position == -1) {
|
||||
break;
|
||||
}
|
||||
buffer.write(onNonMatch(substring(startIndex, position)));
|
||||
buffer.write(onMatch(StringMatch(position, this, from)));
|
||||
startIndex = position + patternLength;
|
||||
}
|
||||
buffer.write(onNonMatch(substring(startIndex)));
|
||||
return buffer.toString();
|
||||
}
|
||||
StringBuffer buffer = StringBuffer();
|
||||
int startIndex = 0;
|
||||
for (Match match in from.allMatches(this)) {
|
||||
buffer.write(onNonMatch(substring(startIndex, match.start)));
|
||||
buffer.write(onMatch(match));
|
||||
startIndex = match.end;
|
||||
}
|
||||
buffer.write(onNonMatch(substring(startIndex)));
|
||||
return buffer.toString();
|
||||
return splitMapJoinImpl(this, from, onMatch, onNonMatch);
|
||||
}
|
||||
|
||||
String _replaceRange(int start, int end, String replacement) {
|
||||
@@ -435,32 +381,7 @@ final class JSStringImpl extends js.JSExternWrapper
|
||||
(re as js.JSValue).toExternRef,
|
||||
);
|
||||
} else {
|
||||
final result = <String>[];
|
||||
// End of most recent match. That is, start of next part to add to result.
|
||||
int start = 0;
|
||||
// Length of most recent match.
|
||||
// Set >0, so no match on the empty string causes the result to be [""].
|
||||
int length = 1;
|
||||
for (var match in pattern.allMatches(this)) {
|
||||
int matchStart = match.start;
|
||||
int matchEnd = match.end;
|
||||
length = matchEnd - matchStart;
|
||||
if (length == 0 && start == matchStart) {
|
||||
// An empty match right after another match is ignored.
|
||||
// This includes an empty match at the start of the string.
|
||||
continue;
|
||||
}
|
||||
int end = matchStart;
|
||||
result.add(substring(start, end));
|
||||
start = matchEnd;
|
||||
}
|
||||
if (start < this.length || length > 0) {
|
||||
// An empty match at the end of the string does not cause a "" at the
|
||||
// end. A non-empty match ending at the end of the string does add a
|
||||
// "".
|
||||
result.add(substring(start));
|
||||
}
|
||||
return result;
|
||||
return genericSplitImpl(this, pattern);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -514,98 +435,6 @@ final class JSStringImpl extends js.JSExternWrapper
|
||||
: JSStringImpl.fromRefUnchecked(upperCaseRef);
|
||||
}
|
||||
|
||||
// Characters with Whitespace property (Unicode 6.3).
|
||||
// 0009..000D ; White_Space # Cc <control-0009>..<control-000D>
|
||||
// 0020 ; White_Space # Zs SPACE
|
||||
// 0085 ; White_Space # Cc <control-0085>
|
||||
// 00A0 ; White_Space # Zs NO-BREAK SPACE
|
||||
// 1680 ; White_Space # Zs OGHAM SPACE MARK
|
||||
// 2000..200A ; White_Space # Zs EN QUAD..HAIR SPACE
|
||||
// 2028 ; White_Space # Zl LINE SEPARATOR
|
||||
// 2029 ; White_Space # Zp PARAGRAPH SEPARATOR
|
||||
// 202F ; White_Space # Zs NARROW NO-BREAK SPACE
|
||||
// 205F ; White_Space # Zs MEDIUM MATHEMATICAL SPACE
|
||||
// 3000 ; White_Space # Zs IDEOGRAPHIC SPACE
|
||||
//
|
||||
// BOM: 0xFEFF
|
||||
static bool _isWhitespace(int codeUnit) {
|
||||
// Most codeUnits should be less than 256. Special case with a smaller
|
||||
// switch.
|
||||
if (codeUnit < 256) {
|
||||
switch (codeUnit) {
|
||||
case 0x09:
|
||||
case 0x0A:
|
||||
case 0x0B:
|
||||
case 0x0C:
|
||||
case 0x0D:
|
||||
case 0x20:
|
||||
case 0x85:
|
||||
case 0xA0:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
switch (codeUnit) {
|
||||
case 0x1680:
|
||||
case 0x2000:
|
||||
case 0x2001:
|
||||
case 0x2002:
|
||||
case 0x2003:
|
||||
case 0x2004:
|
||||
case 0x2005:
|
||||
case 0x2006:
|
||||
case 0x2007:
|
||||
case 0x2008:
|
||||
case 0x2009:
|
||||
case 0x200A:
|
||||
case 0x2028:
|
||||
case 0x2029:
|
||||
case 0x202F:
|
||||
case 0x205F:
|
||||
case 0x3000:
|
||||
case 0xFEFF:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static const int spaceCodeUnit = 0x20;
|
||||
static const int carriageReturnCodeUnit = 0x0D;
|
||||
static const int nelCodeUnit = 0x85;
|
||||
|
||||
/// Finds the index of the first non-whitespace character, or the
|
||||
/// end of the string. Start looking at position [index].
|
||||
static int _skipLeadingWhitespace(JSStringImpl string, int index) {
|
||||
final stringLength = string.length;
|
||||
while (index < stringLength) {
|
||||
int codeUnit = string._codeUnitAtUnchecked(index);
|
||||
if (codeUnit != spaceCodeUnit &&
|
||||
codeUnit != carriageReturnCodeUnit &&
|
||||
!_isWhitespace(codeUnit)) {
|
||||
break;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
/// Finds the index after the last non-whitespace character, or 0.
|
||||
/// Start looking at position [index - 1].
|
||||
static int _skipTrailingWhitespace(JSStringImpl string, int index) {
|
||||
while (index > 0) {
|
||||
int codeUnit = string._codeUnitAtUnchecked(index - 1);
|
||||
if (codeUnit != spaceCodeUnit &&
|
||||
codeUnit != carriageReturnCodeUnit &&
|
||||
!_isWhitespace(codeUnit)) {
|
||||
break;
|
||||
}
|
||||
index--;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
// dart2wasm can't use JavaScript trim directly, because JavaScript does not
|
||||
// trim the NEXT LINE (NEL) character (0x85).
|
||||
@override
|
||||
@@ -625,7 +454,7 @@ final class JSStringImpl extends js.JSExternWrapper
|
||||
final int firstCode = result._codeUnitAtUnchecked(0);
|
||||
int startIndex = 0;
|
||||
if (firstCode == nelCodeUnit) {
|
||||
startIndex = _skipLeadingWhitespace(result, 1);
|
||||
startIndex = skipLeadingWhitespace(result, 1);
|
||||
if (startIndex == resultLength) return "";
|
||||
}
|
||||
|
||||
@@ -635,7 +464,7 @@ final class JSStringImpl extends js.JSExternWrapper
|
||||
// Therefore we don't need to verify that endIndex > startIndex.
|
||||
final int lastCode = result.codeUnitAt(endIndex - 1);
|
||||
if (lastCode == nelCodeUnit) {
|
||||
endIndex = _skipTrailingWhitespace(result, endIndex - 1);
|
||||
endIndex = skipTrailingWhitespace(result, endIndex - 1);
|
||||
}
|
||||
|
||||
if (startIndex == 0 && endIndex == resultLength) {
|
||||
@@ -664,7 +493,7 @@ final class JSStringImpl extends js.JSExternWrapper
|
||||
// Check NEL.
|
||||
int firstCode = result._codeUnitAtUnchecked(0);
|
||||
if (firstCode == nelCodeUnit) {
|
||||
startIndex = _skipLeadingWhitespace(result, 1);
|
||||
startIndex = skipLeadingWhitespace(result, 1);
|
||||
}
|
||||
|
||||
if (startIndex == 0) {
|
||||
@@ -692,7 +521,7 @@ final class JSStringImpl extends js.JSExternWrapper
|
||||
int endIndex = resultLength;
|
||||
int lastCode = result.codeUnitAt(endIndex - 1);
|
||||
if (lastCode == nelCodeUnit) {
|
||||
endIndex = _skipTrailingWhitespace(result, endIndex - 1);
|
||||
endIndex = skipTrailingWhitespace(result, endIndex - 1);
|
||||
}
|
||||
|
||||
if (endIndex == resultLength) {
|
||||
@@ -850,33 +679,8 @@ final class JSStringImpl extends js.JSExternWrapper
|
||||
|
||||
@override
|
||||
String toString() => this;
|
||||
|
||||
int firstNonWhitespace() {
|
||||
final length = this.length;
|
||||
int first = 0;
|
||||
for (; first < length; first++) {
|
||||
if (!_isWhitespace(_codeUnitAtUnchecked(first))) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return first;
|
||||
}
|
||||
|
||||
int lastNonWhitespace() {
|
||||
int last = length - 1;
|
||||
for (; last >= 0; last--) {
|
||||
if (!_isWhitespace(_codeUnitAtUnchecked(last))) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return last;
|
||||
}
|
||||
}
|
||||
|
||||
String _matchString(Match match) => match[0]!;
|
||||
|
||||
String _stringIdentity(String string) => string;
|
||||
|
||||
// NOTE: The [replacement] does not need special escaping it will be used
|
||||
// as-is (due to passing `() => r` to the JS `replaceAll()` call)
|
||||
JSStringImpl _jsStringReplaceAll(
|
||||
|
||||
@@ -2,10 +2,11 @@
|
||||
// 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 dart._string_match;
|
||||
library dart._string_helper;
|
||||
|
||||
import "dart:_error_utils";
|
||||
import "dart:_internal" show IterableElementError;
|
||||
import "dart:_internal" show IterableElementError, unsafeCast;
|
||||
import 'dart:_string' show StringUncheckedOperations;
|
||||
|
||||
class StringMatch implements Match {
|
||||
const StringMatch(this.start, this.input, this.pattern);
|
||||
@@ -96,3 +97,193 @@ int stringFinalizeHash(int hash) {
|
||||
hash &= 0x3FFFFFFF;
|
||||
return hash == 0 ? 1 : hash;
|
||||
}
|
||||
|
||||
String splitMapJoinImpl(
|
||||
String source,
|
||||
Pattern from,
|
||||
String Function(Match)? onMatch,
|
||||
String Function(String)? onNonMatch,
|
||||
) {
|
||||
if (onMatch == null) onMatch = _matchString;
|
||||
if (onNonMatch == null) onNonMatch = _stringIdentity;
|
||||
if (from is String) {
|
||||
final patternLength = from.length;
|
||||
if (patternLength == 0) {
|
||||
// Pattern is the empty string.
|
||||
StringBuffer buffer = StringBuffer();
|
||||
int i = 0;
|
||||
buffer.write(onNonMatch(""));
|
||||
final length = source.length;
|
||||
while (i < length) {
|
||||
buffer.write(onMatch(StringMatch(i, source, "")));
|
||||
// Special case to avoid splitting a surrogate pair.
|
||||
int code = source.codeUnitAt(i);
|
||||
if ((code & ~0x3FF) == 0xD800 && length > i + 1) {
|
||||
// Leading surrogate;
|
||||
code = source.codeUnitAt(i + 1);
|
||||
if ((code & ~0x3FF) == 0xDC00) {
|
||||
// Matching trailing surrogate.
|
||||
buffer.write(onNonMatch(source.substring(i, i + 2)));
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
buffer.write(onNonMatch(source[i]));
|
||||
i++;
|
||||
}
|
||||
buffer.write(onMatch(StringMatch(i, source, "")));
|
||||
buffer.write(onNonMatch(""));
|
||||
return buffer.toString();
|
||||
}
|
||||
StringBuffer buffer = StringBuffer();
|
||||
int startIndex = 0;
|
||||
final length = source.length;
|
||||
while (startIndex < length) {
|
||||
int position = source.indexOf(from, startIndex);
|
||||
if (position == -1) {
|
||||
break;
|
||||
}
|
||||
buffer.write(onNonMatch(source.substring(startIndex, position)));
|
||||
buffer.write(onMatch(StringMatch(position, source, from)));
|
||||
startIndex = position + patternLength;
|
||||
}
|
||||
buffer.write(onNonMatch(source.substring(startIndex)));
|
||||
return buffer.toString();
|
||||
}
|
||||
StringBuffer buffer = StringBuffer();
|
||||
int startIndex = 0;
|
||||
for (Match match in from.allMatches(source)) {
|
||||
buffer.write(onNonMatch(source.substring(startIndex, match.start)));
|
||||
buffer.write(onMatch(match));
|
||||
startIndex = match.end;
|
||||
}
|
||||
buffer.write(onNonMatch(source.substring(startIndex)));
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
/// Implementation of [String.split] for patterns where no specialized JS
|
||||
/// implementation exists.
|
||||
List<String> genericSplitImpl(String source, Pattern pattern) {
|
||||
final result = <String>[];
|
||||
// End of most recent match. That is, start of next part to add to result.
|
||||
int start = 0;
|
||||
// Length of most recent match.
|
||||
// Set >0, so no match on the empty string causes the result to be [""].
|
||||
int length = 1;
|
||||
for (var match in pattern.allMatches(source)) {
|
||||
int matchStart = match.start;
|
||||
int matchEnd = match.end;
|
||||
length = matchEnd - matchStart;
|
||||
if (length == 0 && start == matchStart) {
|
||||
// An empty match right after another match is ignored.
|
||||
// This includes an empty match at the start of the string.
|
||||
continue;
|
||||
}
|
||||
int end = matchStart;
|
||||
result.add(source.substring(start, end));
|
||||
start = matchEnd;
|
||||
}
|
||||
if (start < source.length || length > 0) {
|
||||
// An empty match at the end of the string does not cause a "" at the
|
||||
// end. A non-empty match ending at the end of the string does add a
|
||||
// "".
|
||||
result.add(source.substring(start));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Characters with Whitespace property (Unicode 6.3).
|
||||
// 0009..000D ; White_Space # Cc <control-0009>..<control-000D>
|
||||
// 0020 ; White_Space # Zs SPACE
|
||||
// 0085 ; White_Space # Cc <control-0085>
|
||||
// 00A0 ; White_Space # Zs NO-BREAK SPACE
|
||||
// 1680 ; White_Space # Zs OGHAM SPACE MARK
|
||||
// 2000..200A ; White_Space # Zs EN QUAD..HAIR SPACE
|
||||
// 2028 ; White_Space # Zl LINE SEPARATOR
|
||||
// 2029 ; White_Space # Zp PARAGRAPH SEPARATOR
|
||||
// 202F ; White_Space # Zs NARROW NO-BREAK SPACE
|
||||
// 205F ; White_Space # Zs MEDIUM MATHEMATICAL SPACE
|
||||
// 3000 ; White_Space # Zs IDEOGRAPHIC SPACE
|
||||
//
|
||||
// BOM: 0xFEFF
|
||||
bool isWhitespace(int codeUnit) {
|
||||
// Most codeUnits should be less than 256. Special case with a smaller
|
||||
// switch.
|
||||
if (codeUnit < 256) {
|
||||
switch (codeUnit) {
|
||||
case 0x09:
|
||||
case 0x0A:
|
||||
case 0x0B:
|
||||
case 0x0C:
|
||||
case 0x0D:
|
||||
case 0x20:
|
||||
case 0x85:
|
||||
case 0xA0:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
switch (codeUnit) {
|
||||
case 0x1680:
|
||||
case 0x2000:
|
||||
case 0x2001:
|
||||
case 0x2002:
|
||||
case 0x2003:
|
||||
case 0x2004:
|
||||
case 0x2005:
|
||||
case 0x2006:
|
||||
case 0x2007:
|
||||
case 0x2008:
|
||||
case 0x2009:
|
||||
case 0x200A:
|
||||
case 0x2028:
|
||||
case 0x2029:
|
||||
case 0x202F:
|
||||
case 0x205F:
|
||||
case 0x3000:
|
||||
case 0xFEFF:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const int spaceCodeUnit = 0x20;
|
||||
const int carriageReturnCodeUnit = 0x0D;
|
||||
const int nelCodeUnit = 0x85;
|
||||
|
||||
/// Finds the index of the first non-whitespace character, or the
|
||||
/// end of the string. Start looking at position [index].
|
||||
int skipLeadingWhitespace(String string, int index) {
|
||||
final stringLength = string.length;
|
||||
while (index < stringLength) {
|
||||
int codeUnit = string.codeUnitAtUnchecked(index);
|
||||
if (codeUnit != spaceCodeUnit &&
|
||||
codeUnit != carriageReturnCodeUnit &&
|
||||
!isWhitespace(codeUnit)) {
|
||||
break;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
/// Finds the index after the last non-whitespace character, or 0.
|
||||
/// Start looking at position [index - 1] to [from].
|
||||
int skipTrailingWhitespace(String string, int index, [int from = 0]) {
|
||||
while (index > from) {
|
||||
int codeUnit = string.codeUnitAtUnchecked(index - 1);
|
||||
if (codeUnit != spaceCodeUnit &&
|
||||
codeUnit != carriageReturnCodeUnit &&
|
||||
!isWhitespace(codeUnit)) {
|
||||
break;
|
||||
}
|
||||
index--;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
String _matchString(Match match) => match[0]!;
|
||||
|
||||
String _stringIdentity(String string) => string;
|
||||
|
||||
@@ -208,10 +208,3 @@ class String {
|
||||
return JSStringImpl.fromCodePoint(charCode);
|
||||
}
|
||||
}
|
||||
|
||||
extension _StringExt on String {
|
||||
int firstNonWhitespace() =>
|
||||
unsafeCast<JSStringImpl>(this).firstNonWhitespace();
|
||||
|
||||
int lastNonWhitespace() => unsafeCast<JSStringImpl>(this).lastNonWhitespace();
|
||||
}
|
||||
|
||||
@@ -120,10 +120,3 @@ class String {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension _StringExt on String {
|
||||
int firstNonWhitespace() =>
|
||||
unsafeCast<JSStringImpl>(this).firstNonWhitespace();
|
||||
|
||||
int lastNonWhitespace() => unsafeCast<JSStringImpl>(this).lastNonWhitespace();
|
||||
}
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
|
||||
import "dart:_embedder" as embedder;
|
||||
import "dart:_internal" show patch, unsafeCast;
|
||||
import "dart:_js_helper" show jsStringFromDartString, JSExternWrapperExt;
|
||||
import "dart:_string";
|
||||
import "dart:_string_helper";
|
||||
import "dart:_wasm";
|
||||
|
||||
@patch
|
||||
@@ -21,12 +21,13 @@ class _Utf8Decoder {
|
||||
class _StringParser {
|
||||
@patch
|
||||
static WasmArray<WasmI16> stringToCharCodeArray(String string, int end) {
|
||||
final externRef = unsafeCast<JSStringImpl>(string).wrappedExternRef;
|
||||
final array = WasmArray<WasmI16>(end);
|
||||
if (string.length == end) {
|
||||
jsStringIntoCharCodeArray(externRef, array, 0.toWasmI32());
|
||||
final externRef = unsafeCast<JSStringImpl>(string).wrappedExternRef;
|
||||
embedder.stringToCodeUnits(externRef, array, 0.toWasmI32());
|
||||
} else {
|
||||
for (int i = 0; i < end; ++i) array.write(i, jsCharCodeAt(externRef, i));
|
||||
for (int i = 0; i < end; ++i)
|
||||
array.write(i, string.codeUnitAtUnchecked(i));
|
||||
}
|
||||
|
||||
return array;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
import 'dart:_embedder' as embedder;
|
||||
import 'dart:_internal' show patch;
|
||||
import 'dart:_js_helper' show jsStringFromDartString, JSExternWrapperExt;
|
||||
import 'dart:_string' show embedderStringFromDartString;
|
||||
import 'dart:_wasm';
|
||||
import 'dart:async' show Zone;
|
||||
import 'dart:isolate';
|
||||
@@ -15,7 +15,7 @@ bool debugger({bool when = true, String? message}) {
|
||||
embedder.debugger(
|
||||
message == null
|
||||
? WasmExternRef.nullRef
|
||||
: jsStringFromDartString(message).wrappedExternRef,
|
||||
: embedderStringFromDartString(message).wrappedExternRef,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -193,7 +193,7 @@ void _reportTaskEvent(
|
||||
WasmI32.fromInt(taskId),
|
||||
WasmI32.fromInt(flowId),
|
||||
WasmI32.fromInt(type),
|
||||
jsStringFromDartString(name).wrappedExternRef,
|
||||
jsStringFromDartString(argumentsAsJson).wrappedExternRef,
|
||||
embedderStringFromDartString(name).wrappedExternRef,
|
||||
embedderStringFromDartString(argumentsAsJson).wrappedExternRef,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
import 'dart:_embedder';
|
||||
import 'dart:_internal' show patch;
|
||||
import 'dart:_js_helper';
|
||||
import 'dart:_string';
|
||||
import 'dart:_wasm';
|
||||
|
||||
@patch
|
||||
@@ -21,7 +21,7 @@ class double {
|
||||
@patch
|
||||
static double? tryParse(String source) {
|
||||
final parseResult = doubleTryParse(
|
||||
jsStringFromDartString(source).wrappedExternRef,
|
||||
embedderStringFromDartString(source).wrappedExternRef,
|
||||
);
|
||||
if (parseResult.isNull) {
|
||||
return null;
|
||||
|
||||
@@ -74,6 +74,75 @@ external WasmExternRef stringFromAsciiBytes(
|
||||
WasmI32 length,
|
||||
);
|
||||
|
||||
@pragma("wasm:import", "dart.stringLength")
|
||||
external WasmI32 stringLength(WasmExternRef? string);
|
||||
@pragma("wasm:import", "dart.stringEquals")
|
||||
external WasmI32 stringEquals(WasmExternRef? a, WasmExternRef? b);
|
||||
@pragma("wasm:import", "dart.stringCompare")
|
||||
external WasmI32 stringCompare(WasmExternRef? a, WasmExternRef? b);
|
||||
@pragma("wasm:import", "dart.stringCodeUnitAt")
|
||||
external WasmI32 stringCodeUnitAt(WasmExternRef? a, WasmI32 index);
|
||||
@pragma("wasm:import", "dart.stringIndexOfString")
|
||||
external WasmI32 stringIndexOfString(
|
||||
WasmExternRef? a,
|
||||
WasmExternRef? b,
|
||||
WasmI32 start,
|
||||
);
|
||||
@pragma("wasm:import", "dart.stringLastIndexOfString")
|
||||
external WasmI32 stringLastIndexOfString(
|
||||
WasmExternRef? a,
|
||||
WasmExternRef? b,
|
||||
WasmI32 start,
|
||||
);
|
||||
|
||||
/// Specialization for [String.replaceAll] where the pattern is an embedder-
|
||||
/// managed string.
|
||||
@pragma("wasm:import", "dart.stringReplaceAllString")
|
||||
external WasmExternRef? stringReplaceAllString(
|
||||
WasmExternRef? string,
|
||||
WasmExternRef? needle,
|
||||
WasmExternRef? replacement,
|
||||
);
|
||||
|
||||
/// Specialization for [String.replaceAll] where the pattern is an embedder-
|
||||
/// managed regular expression.
|
||||
@pragma("wasm:import", "dart.stringReplaceAllRegExp")
|
||||
external WasmExternRef? stringReplaceAllRegExp(
|
||||
WasmExternRef? string,
|
||||
WasmExternRef? needle,
|
||||
WasmExternRef? replacement,
|
||||
);
|
||||
@pragma("wasm:import", "dart.stringSubstring")
|
||||
external WasmExternRef? stringSubstring(
|
||||
WasmExternRef? a,
|
||||
WasmI32 start,
|
||||
WasmI32 end,
|
||||
);
|
||||
@pragma("wasm:import", "dart.stringToLowerCase")
|
||||
external WasmExternRef? stringToLowerCase(WasmExternRef? string);
|
||||
@pragma("wasm:import", "dart.stringToUpperCase")
|
||||
external WasmExternRef? stringToUpperCase(WasmExternRef? string);
|
||||
@pragma("wasm:import", "dart.stringConcat")
|
||||
external WasmExternRef? stringConcat(WasmExternRef? a, WasmExternRef? b);
|
||||
@pragma("wasm:import", "dart.stringRepeat")
|
||||
external WasmExternRef? stringRepeat(WasmExternRef? string, WasmI32 times);
|
||||
@pragma("wasm:import", "dart.stringReplaceRange")
|
||||
external WasmExternRef? stringReplaceRange(
|
||||
WasmExternRef? string,
|
||||
WasmI32 start,
|
||||
WasmI32 end,
|
||||
WasmExternRef? replacement,
|
||||
);
|
||||
|
||||
/// Writes code units of [string] into [outArray], starting at array position
|
||||
/// [startIndex].
|
||||
@pragma("wasm:import", "dart.stringToCodeUnits")
|
||||
external WasmVoid stringToCodeUnits(
|
||||
WasmExternRef? string,
|
||||
WasmArray<WasmI16> outArray,
|
||||
WasmI32 startIndex,
|
||||
);
|
||||
|
||||
/// Get the frequency of ticks reported by [monotonicClockTicks] in Hz.
|
||||
///
|
||||
/// Currently, the only supported values are 1kHz and 1MHz. Attempting to use
|
||||
|
||||
@@ -0,0 +1,532 @@
|
||||
// Copyright (c) 2026, 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 'dart:_embedder' as embedder;
|
||||
import 'dart:_error_utils';
|
||||
import 'dart:_internal';
|
||||
import 'dart:_object_helper';
|
||||
import 'dart:_string_helper';
|
||||
import 'dart:_wasm';
|
||||
|
||||
part 'regexp.dart';
|
||||
|
||||
abstract class StringUncheckedOperationsBase {
|
||||
int _codeUnitAtUnchecked(int index);
|
||||
String _substringUnchecked(int start, int end);
|
||||
}
|
||||
|
||||
extension StringUncheckedOperations on String {
|
||||
@pragma('wasm:prefer-inline')
|
||||
int codeUnitAtUnchecked(int index) =>
|
||||
unsafeCast<StringUncheckedOperationsBase>(
|
||||
this,
|
||||
)._codeUnitAtUnchecked(index);
|
||||
|
||||
@pragma('wasm:prefer-inline')
|
||||
String substringUnchecked(int start, int end) =>
|
||||
unsafeCast<StringUncheckedOperationsBase>(
|
||||
this,
|
||||
)._substringUnchecked(start, end);
|
||||
}
|
||||
|
||||
/// A string managed by the WebAssembly embedder.
|
||||
///
|
||||
/// This is not necessarily a JavaScript string, but the `JSStringImpl` name is
|
||||
/// referenced a lot in the compiler and since this class and it's counterpart
|
||||
/// in the JS target have the same structure (wrapping an externref), adopting
|
||||
/// the same name avoids conditional names in the compiler.
|
||||
final class JSStringImpl implements String, StringUncheckedOperationsBase {
|
||||
WasmExternRef? _ref;
|
||||
|
||||
JSStringImpl.fromRefUnchecked(this._ref);
|
||||
|
||||
WasmExternRef? get wrappedExternRef => _ref;
|
||||
|
||||
@override
|
||||
@pragma("wasm:prefer-inline")
|
||||
int get length => embedder.stringLength(_ref).toIntUnsigned();
|
||||
|
||||
@override
|
||||
@pragma("wasm:prefer-inline")
|
||||
bool get isEmpty => length == 0;
|
||||
|
||||
@override
|
||||
@pragma("wasm:prefer-inline")
|
||||
bool get isNotEmpty => !isEmpty;
|
||||
|
||||
@pragma("wasm:entry-point")
|
||||
static String _interpolate(WasmArray<Object?> values) {
|
||||
final valuesLength = values.length;
|
||||
final result = StringBuffer();
|
||||
for (int i = 0; i < valuesLength; i++) {
|
||||
result.write(values[i].toString());
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
@pragma("wasm:entry-point", "call")
|
||||
static String _interpolate1(Object? value) {
|
||||
return value is String ? value : value.toString();
|
||||
}
|
||||
|
||||
@pragma("wasm:entry-point", "call")
|
||||
static String _interpolate2(Object? value1, Object? value2) {
|
||||
return (StringBuffer(
|
||||
value1 is String ? value1 : value1.toString(),
|
||||
)..write(value2 is String ? value2 : value2.toString())).toString();
|
||||
}
|
||||
|
||||
@pragma("wasm:entry-point", "call")
|
||||
static String _interpolate3(Object? value1, Object? value2, Object? value3) {
|
||||
return (StringBuffer(value1 is String ? value1 : value1.toString())
|
||||
..write(value2 is String ? value2 : value2.toString())
|
||||
..write(value3 is String ? value3 : value3.toString()))
|
||||
.toString();
|
||||
}
|
||||
|
||||
@pragma("wasm:entry-point", "call")
|
||||
static String _interpolate4(
|
||||
Object? value1,
|
||||
Object? value2,
|
||||
Object? value3,
|
||||
Object? value4,
|
||||
) {
|
||||
return (StringBuffer(value1 is String ? value1 : value1.toString())
|
||||
..write(value2 is String ? value2 : value2.toString())
|
||||
..write(value3 is String ? value3 : value3.toString())
|
||||
..write(value4 is String ? value4 : value4.toString()))
|
||||
.toString();
|
||||
}
|
||||
|
||||
static JSStringImpl fromAsciiBytes(
|
||||
WasmArray<WasmI8> source,
|
||||
int start,
|
||||
int end,
|
||||
) {
|
||||
final length = WasmI32.fromInt(end - start);
|
||||
return JSStringImpl.fromRefUnchecked(
|
||||
embedder.stringFromAsciiBytes(source, WasmI32.fromInt(start), length),
|
||||
);
|
||||
}
|
||||
|
||||
static JSStringImpl fromCharCodeArray(
|
||||
WasmArray<WasmI16> source,
|
||||
int start,
|
||||
int end,
|
||||
) {
|
||||
return JSStringImpl.fromRefUnchecked(
|
||||
embedder.stringFromCharCodeArray(
|
||||
source,
|
||||
WasmI32.fromInt(start),
|
||||
WasmI32.fromInt(end - start),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@pragma("wasm:initialize-at-startup")
|
||||
static final _stringFromCodePointBuffer = WasmArray<WasmI16>(2);
|
||||
|
||||
static JSStringImpl fromCharCode(int charCode) {
|
||||
final array = _stringFromCodePointBuffer;
|
||||
array.write(0, charCode);
|
||||
return JSStringImpl.fromCharCodeArray(array, 0, 1);
|
||||
}
|
||||
|
||||
static JSStringImpl fromCodePoint(int codePoint) {
|
||||
final array = _stringFromCodePointBuffer;
|
||||
if (codePoint <= 0xffff) {
|
||||
array.write(0, codePoint);
|
||||
return JSStringImpl.fromCharCodeArray(array, 0, 1);
|
||||
}
|
||||
final low = 0xDC00 | (codePoint & 0x3ff);
|
||||
final high = 0xD7C0 + (codePoint >> 10);
|
||||
array.write(0, high);
|
||||
array.write(1, low);
|
||||
return JSStringImpl.fromCharCodeArray(array, 0, 2);
|
||||
}
|
||||
|
||||
@override
|
||||
@pragma("wasm:prefer-inline")
|
||||
int codeUnitAt(int index) {
|
||||
final length = this.length;
|
||||
IndexErrorUtils.checkIndex(index, length);
|
||||
return _codeUnitAtUnchecked(index);
|
||||
}
|
||||
|
||||
@override
|
||||
@pragma("wasm:prefer-inline")
|
||||
int _codeUnitAtUnchecked(int index) {
|
||||
return embedder
|
||||
.stringCodeUnitAt(wrappedExternRef, WasmI32.fromInt(index))
|
||||
.toIntUnsigned();
|
||||
}
|
||||
|
||||
@override
|
||||
Iterable<Match> allMatches(String string, [int start = 0]) {
|
||||
final stringLength = string.length;
|
||||
RangeErrorUtils.checkValueBetweenZeroAndPositiveMax(start, stringLength);
|
||||
return StringAllMatchesIterable(string, this, start);
|
||||
}
|
||||
|
||||
@override
|
||||
Match? matchAsPrefix(String string, [int start = 0]) {
|
||||
final stringLength = string.length;
|
||||
RangeErrorUtils.checkValueBetweenZeroAndPositiveMax(start, stringLength);
|
||||
final length = this.length;
|
||||
if (start + length > stringLength) return null;
|
||||
// TODO(lrn): See if this can be optimized.
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (string.codeUnitAt(start + i) != codeUnitAt(i)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return StringMatch(start, string, this);
|
||||
}
|
||||
|
||||
@override
|
||||
@pragma('wasm:pure-function')
|
||||
String operator +(String other) {
|
||||
return JSStringImpl.fromRefUnchecked(
|
||||
embedder.stringConcat(
|
||||
wrappedExternRef,
|
||||
unsafeCast<JSStringImpl>(other).wrappedExternRef,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
bool endsWith(String other) {
|
||||
final otherLength = other.length;
|
||||
final length = this.length;
|
||||
if (otherLength > length) return false;
|
||||
return other == _substringUnchecked(length - otherLength, length);
|
||||
}
|
||||
|
||||
@override
|
||||
String replaceAll(Pattern from, String to) {
|
||||
if (from is String) {
|
||||
if (from.isEmpty) {
|
||||
if (isEmpty) return to;
|
||||
StringBuffer result = StringBuffer();
|
||||
result.write(to);
|
||||
final length = this.length;
|
||||
for (int i = 0; i < length; i++) {
|
||||
result.write(this[i]);
|
||||
result.write(to);
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
return JSStringImpl.fromRefUnchecked(
|
||||
embedder.stringReplaceAllString(
|
||||
wrappedExternRef,
|
||||
unsafeCast<JSStringImpl>(from).wrappedExternRef,
|
||||
unsafeCast<JSStringImpl>(to).wrappedExternRef,
|
||||
),
|
||||
);
|
||||
} else if (from is EmbedderRegExp) {
|
||||
return JSStringImpl.fromRefUnchecked(
|
||||
embedder.stringReplaceAllRegExp(
|
||||
wrappedExternRef,
|
||||
from._regexp,
|
||||
unsafeCast<JSStringImpl>(to).wrappedExternRef,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
int startIndex = 0;
|
||||
StringBuffer result = StringBuffer();
|
||||
for (Match match in from.allMatches(this)) {
|
||||
result.write(substring(startIndex, match.start));
|
||||
result.write(to);
|
||||
startIndex = match.end;
|
||||
}
|
||||
result.write(substring(startIndex));
|
||||
return result.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
String replaceAllMapped(Pattern from, String Function(Match) convert) {
|
||||
return splitMapJoin(from, onMatch: convert);
|
||||
}
|
||||
|
||||
@override
|
||||
String splitMapJoin(
|
||||
Pattern from, {
|
||||
String Function(Match)? onMatch,
|
||||
String Function(String)? onNonMatch,
|
||||
}) {
|
||||
return splitMapJoinImpl(this, from, onMatch, onNonMatch);
|
||||
}
|
||||
|
||||
String _replaceRange(int start, int end, String replacement) {
|
||||
return JSStringImpl.fromRefUnchecked(
|
||||
embedder.stringReplaceRange(
|
||||
wrappedExternRef,
|
||||
WasmI32.fromInt(start),
|
||||
WasmI32.fromInt(end),
|
||||
unsafeCast<JSStringImpl>(replacement).wrappedExternRef,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String replaceFirst(Pattern from, String to, [int startIndex = 0]) {
|
||||
Iterator<Match> matches = from.allMatches(this, startIndex).iterator;
|
||||
if (!matches.moveNext()) return this;
|
||||
Match match = matches.current;
|
||||
return replaceRange(match.start, match.end, to);
|
||||
}
|
||||
|
||||
@override
|
||||
String replaceFirstMapped(
|
||||
Pattern from,
|
||||
String replace(Match match), [
|
||||
int startIndex = 0,
|
||||
]) {
|
||||
RangeErrorUtils.checkValueBetweenZeroAndPositiveMax(startIndex, length);
|
||||
final Iterator<Match> matches = from.allMatches(this, startIndex).iterator;
|
||||
if (!matches.moveNext()) return this;
|
||||
final Match match = matches.current;
|
||||
return replaceRange(match.start, match.end, replace(match));
|
||||
}
|
||||
|
||||
@override
|
||||
List<String> split(Pattern pattern) {
|
||||
return genericSplitImpl(this, pattern);
|
||||
}
|
||||
|
||||
@override
|
||||
String replaceRange(int start, int? end, String replacement) {
|
||||
end ??= length;
|
||||
RangeErrorUtils.checkValidRange(start, end, length);
|
||||
return _replaceRange(start, end, replacement);
|
||||
}
|
||||
|
||||
@override
|
||||
bool startsWith(Pattern pattern, [int index = 0]) {
|
||||
RangeErrorUtils.checkValueBetweenZeroAndPositiveMax(index, length);
|
||||
if (pattern is String) {
|
||||
final patternLength = pattern.length;
|
||||
final endIndex = index + patternLength;
|
||||
if (endIndex > length) return false;
|
||||
return pattern == substring(index, endIndex);
|
||||
}
|
||||
return pattern.matchAsPrefix(this, index) != null;
|
||||
}
|
||||
|
||||
@override
|
||||
String substring(int start, [int? end]) {
|
||||
end ??= length;
|
||||
RangeErrorUtils.checkValidRange(start, end, length);
|
||||
if (start == end) return "";
|
||||
return _substringUnchecked(start, end);
|
||||
}
|
||||
|
||||
@override
|
||||
@pragma('wasm:prefer-inline')
|
||||
String _substringUnchecked(int start, int end) =>
|
||||
JSStringImpl.fromRefUnchecked(
|
||||
embedder.stringSubstring(
|
||||
wrappedExternRef,
|
||||
WasmI32.fromInt(start),
|
||||
WasmI32.fromInt(end),
|
||||
),
|
||||
);
|
||||
|
||||
@override
|
||||
String toLowerCase() {
|
||||
final toLower = embedder.stringToLowerCase(wrappedExternRef);
|
||||
if (embedder.stringEquals(toLower, wrappedExternRef).toBool()) {
|
||||
return this;
|
||||
} else {
|
||||
return JSStringImpl.fromRefUnchecked(toLower);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
String toUpperCase() {
|
||||
final toUpper = embedder.stringToUpperCase(wrappedExternRef);
|
||||
if (embedder.stringEquals(toUpper, wrappedExternRef).toBool()) {
|
||||
return this;
|
||||
} else {
|
||||
return JSStringImpl.fromRefUnchecked(toUpper);
|
||||
}
|
||||
}
|
||||
|
||||
String _trim(bool left, bool right) {
|
||||
if (isEmpty) return this;
|
||||
|
||||
var start = 0, end = length;
|
||||
if (left) start = skipLeadingWhitespace(this, 0);
|
||||
if (right) end = skipTrailingWhitespace(this, length, start);
|
||||
if (start >= end) return '';
|
||||
if (start == 0 && end == length) return this;
|
||||
|
||||
return _substringUnchecked(start, end);
|
||||
}
|
||||
|
||||
@override
|
||||
String trim() {
|
||||
return _trim(true, true);
|
||||
}
|
||||
|
||||
@override
|
||||
String trimLeft() {
|
||||
return _trim(true, false);
|
||||
}
|
||||
|
||||
@override
|
||||
String trimRight() {
|
||||
return _trim(false, true);
|
||||
}
|
||||
|
||||
@override
|
||||
String operator *(int times) {
|
||||
if (0 >= times) return '';
|
||||
if (times == 1 || length == 0) return this;
|
||||
if (times & 0x7fffffff != times) {
|
||||
throw Exception(
|
||||
'The implementation cannot handle very large operands (was: $times).',
|
||||
);
|
||||
}
|
||||
|
||||
return JSStringImpl.fromRefUnchecked(
|
||||
embedder.stringRepeat(wrappedExternRef, WasmI32.fromInt(times)),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String padLeft(int width, [String padding = ' ']) {
|
||||
int delta = width - length;
|
||||
if (delta <= 0) return this;
|
||||
return (padding * delta) + this;
|
||||
}
|
||||
|
||||
@override
|
||||
String padRight(int width, [String padding = ' ']) {
|
||||
int delta = width - length;
|
||||
if (delta <= 0) return this;
|
||||
return this + (padding * delta);
|
||||
}
|
||||
|
||||
@override
|
||||
List<int> get codeUnits => CodeUnits(this);
|
||||
|
||||
@override
|
||||
Runes get runes => Runes(this);
|
||||
|
||||
@override
|
||||
int indexOf(Pattern pattern, [int start = 0]) {
|
||||
final length = this.length;
|
||||
RangeErrorUtils.checkValueBetweenZeroAndPositiveMax(start, length);
|
||||
if (pattern is JSStringImpl) {
|
||||
return embedder
|
||||
.stringIndexOfString(
|
||||
wrappedExternRef,
|
||||
pattern.wrappedExternRef,
|
||||
WasmI32.fromInt(start),
|
||||
)
|
||||
.toIntSigned();
|
||||
} else if (pattern is EmbedderRegExp) {
|
||||
final match = pattern._search(this, start, false);
|
||||
return match?.start ?? -1;
|
||||
} else {
|
||||
for (int i = start; i <= length; i++) {
|
||||
if (pattern.matchAsPrefix(this, i) != null) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
int lastIndexOf(Pattern pattern, [int? start]) {
|
||||
final length = this.length;
|
||||
if (start == null) {
|
||||
start = length;
|
||||
} else {
|
||||
RangeErrorUtils.checkValueBetweenZeroAndPositiveMax(start, length);
|
||||
}
|
||||
if (pattern is JSStringImpl) {
|
||||
if (start + pattern.length > length) {
|
||||
start = length - pattern.length;
|
||||
}
|
||||
return embedder
|
||||
.stringLastIndexOfString(
|
||||
wrappedExternRef,
|
||||
pattern.wrappedExternRef,
|
||||
WasmI32.fromInt(start),
|
||||
)
|
||||
.toIntSigned();
|
||||
}
|
||||
|
||||
for (int i = start; i >= 0; i--) {
|
||||
if (pattern.matchAsPrefix(this, i) != null) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@override
|
||||
bool contains(Pattern other, [int startIndex = 0]) {
|
||||
final length = this.length;
|
||||
RangeErrorUtils.checkValueBetweenZeroAndPositiveMax(startIndex, length);
|
||||
if (other is String || other is EmbedderRegExp) {
|
||||
return indexOf(other, startIndex) >= 0;
|
||||
} else {
|
||||
return other.allMatches(substring(startIndex)).isNotEmpty;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
int hash = getIdentityHashField(this);
|
||||
if (hash != 0) return hash;
|
||||
hash = _computeHashCode();
|
||||
setIdentityHashField(this, hash);
|
||||
return hash;
|
||||
}
|
||||
|
||||
/// This must be kept in sync with `StringBase.hashCode` in string_patch.dart.
|
||||
int _computeHashCode() {
|
||||
int hash = 0;
|
||||
final length = this.length;
|
||||
for (int i = 0; i < length; i++) {
|
||||
hash = stringCombineHashes(hash, _codeUnitAtUnchecked(i));
|
||||
}
|
||||
return stringFinalizeHash(hash);
|
||||
}
|
||||
|
||||
@override
|
||||
@pragma("wasm:prefer-inline")
|
||||
String operator [](int index) {
|
||||
IndexErrorUtils.checkIndex(index, length);
|
||||
return JSStringImpl.fromCharCode(_codeUnitAtUnchecked(index));
|
||||
}
|
||||
|
||||
@override
|
||||
@pragma('wasm:prefer-inline')
|
||||
bool operator ==(Object other) =>
|
||||
other is JSStringImpl && embedder.stringEquals(_ref, other._ref).toBool();
|
||||
|
||||
@override
|
||||
@pragma('wasm:prefer-inline')
|
||||
int compareTo(String other) => embedder
|
||||
.stringCompare(
|
||||
wrappedExternRef,
|
||||
unsafeCast<JSStringImpl>(other).wrappedExternRef,
|
||||
)
|
||||
.toIntSigned();
|
||||
|
||||
@override
|
||||
String toString() => this;
|
||||
}
|
||||
|
||||
String _matchString(Match match) => match[0]!;
|
||||
|
||||
String _stringIdentity(String string) => string;
|
||||
|
||||
@patch
|
||||
@pragma('wasm:prefer-inline')
|
||||
JSStringImpl embedderStringFromDartString(String s) {
|
||||
return unsafeCast<JSStringImpl>(s);
|
||||
}
|
||||
@@ -3,10 +3,11 @@
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import 'dart:_embedder' as embedder;
|
||||
import "dart:_js_helper" show jsStringFromDartString, JSExternWrapperExt;
|
||||
import 'dart:_string' show JSStringImpl;
|
||||
import 'dart:_string' show embedderStringFromDartString, JSStringImpl;
|
||||
import 'dart:_wasm';
|
||||
|
||||
String jsonEncode(String object) => JSStringImpl.fromRef(
|
||||
embedder.jsonEncodeString(jsStringFromDartString(object).wrappedExternRef),
|
||||
String jsonEncode(String object) => JSStringImpl.fromRefUnchecked(
|
||||
embedder.jsonEncodeString(
|
||||
embedderStringFromDartString(object).wrappedExternRef,
|
||||
),
|
||||
);
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
// Copyright (c) 2026, 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.
|
||||
|
||||
// dart:js_interop is not available on this target, but it's referenced in
|
||||
// dart:_wasm.
|
||||
// TODO(63166): Untangle these libraries.
|
||||
|
||||
typedef JSAny = Never;
|
||||
@@ -3,8 +3,8 @@
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import 'dart:_embedder' as embedder;
|
||||
import 'dart:_js_helper' show jsStringFromDartString, JSExternWrapperExt;
|
||||
import 'dart:_string' show embedderStringFromDartString;
|
||||
|
||||
@patch
|
||||
void printToConsole(String line) =>
|
||||
embedder.print(jsStringFromDartString(line).wrappedExternRef);
|
||||
embedder.print(embedderStringFromDartString(line).wrappedExternRef);
|
||||
|
||||
@@ -0,0 +1,216 @@
|
||||
// Copyright (c) 2026, 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:_string';
|
||||
|
||||
final class EmbedderRegExp implements RegExp {
|
||||
WasmExternRef? _regexp = WasmExternRef.nullRef;
|
||||
|
||||
@override
|
||||
final String pattern;
|
||||
@override
|
||||
final bool isMultiLine;
|
||||
@override
|
||||
final bool isCaseSensitive;
|
||||
@override
|
||||
final bool isUnicode;
|
||||
@override
|
||||
final bool isDotAll;
|
||||
|
||||
EmbedderRegExp(
|
||||
this.pattern,
|
||||
this.isMultiLine,
|
||||
this.isCaseSensitive,
|
||||
this.isUnicode,
|
||||
this.isDotAll,
|
||||
) {
|
||||
final compiled = embedder.regexpCreateOrFailWithString(
|
||||
embedderStringFromDartString(pattern).wrappedExternRef,
|
||||
WasmI32.fromBool(isMultiLine),
|
||||
WasmI32.fromBool(isCaseSensitive),
|
||||
WasmI32.fromBool(isUnicode),
|
||||
WasmI32.fromBool(isDotAll),
|
||||
);
|
||||
if (!embedder.regexpIsRegexp(compiled).toBool()) {
|
||||
// The returned value is the stringified JavaScript exception. Turn it
|
||||
// into a Dart exception.
|
||||
final errorMessage = JSStringImpl.fromRefUnchecked(compiled);
|
||||
throw FormatException('Illegal RegExp pattern ($errorMessage)', pattern);
|
||||
}
|
||||
|
||||
this._regexp = compiled;
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
final buffer = StringBuffer('RegExp/');
|
||||
buffer.write(pattern);
|
||||
buffer.write('/');
|
||||
|
||||
if (isMultiLine) buffer.write('m');
|
||||
if (!isCaseSensitive) buffer.write('i');
|
||||
if (isUnicode) buffer.write('u');
|
||||
if (isDotAll) buffer.write('s');
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
@override
|
||||
Iterable<RegExpMatch> allMatches(String input, [int start = 0]) {
|
||||
RangeErrorUtils.checkValueBetweenZeroAndPositiveMax(start, input.length);
|
||||
return Iterable.withIterator(
|
||||
() => _EmbedderMatchesIterator(this, input, start),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
RegExpMatch? firstMatch(String input) {
|
||||
return _search(input, 0, false);
|
||||
}
|
||||
|
||||
@override
|
||||
bool hasMatch(String input) {
|
||||
return firstMatch(input) != null;
|
||||
}
|
||||
|
||||
@override
|
||||
Match? matchAsPrefix(String string, [int start = 0]) {
|
||||
return _search(string, start, true);
|
||||
}
|
||||
|
||||
_EmbedderMatch? _search(String string, int start, bool exactStartIndex) {
|
||||
RangeErrorUtils.checkValueBetweenZeroAndPositiveMax(start, string.length);
|
||||
final match = embedder.regexpMatch(
|
||||
_regexp,
|
||||
embedderStringFromDartString(string).wrappedExternRef,
|
||||
WasmI32.fromInt(start),
|
||||
WasmI32.fromBool(exactStartIndex),
|
||||
);
|
||||
if (match.isNull) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return _EmbedderMatch(this, string).._match = match;
|
||||
}
|
||||
|
||||
@override
|
||||
String? stringMatch(String input) {
|
||||
var match = firstMatch(input);
|
||||
if (match != null) return match[0];
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
final class _EmbedderMatch implements RegExpMatch {
|
||||
@override
|
||||
final EmbedderRegExp pattern;
|
||||
@override
|
||||
final String input;
|
||||
|
||||
WasmExternRef? _match = WasmExternRef.nullRef;
|
||||
|
||||
_EmbedderMatch(this.pattern, this.input);
|
||||
|
||||
@override
|
||||
String? operator [](int group) {
|
||||
return this.group(group);
|
||||
}
|
||||
|
||||
@override
|
||||
int get start => embedder.regexpMatchGetStart(_match).toIntUnsigned();
|
||||
|
||||
@override
|
||||
int get end => embedder.regexpMatchGetEnd(_match).toIntUnsigned();
|
||||
|
||||
@override
|
||||
int get groupCount =>
|
||||
embedder.regexpMatchGetGroupCount(_match).toIntUnsigned();
|
||||
|
||||
@override
|
||||
String? group(int group) {
|
||||
IndexErrorUtils.checkIndex(group, groupCount + 1);
|
||||
final contents = embedder.regexpMatchGetGroup(
|
||||
_match,
|
||||
WasmI32.fromInt(group),
|
||||
);
|
||||
return contents.isNull ? null : JSStringImpl.fromRefUnchecked(contents);
|
||||
}
|
||||
|
||||
@override
|
||||
List<String?> groups(List<int> groupIndices) {
|
||||
return [for (final index in groupIndices) group(index)];
|
||||
}
|
||||
|
||||
@override
|
||||
late final List<String> groupNames = List.generate(
|
||||
embedder.regexpMatchGetNamedGroups(_match).toIntUnsigned(),
|
||||
(i) {
|
||||
return JSStringImpl.fromRefUnchecked(
|
||||
embedder.regexpMatchGetGroupName(_match, WasmI32.fromInt(i)),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
@override
|
||||
String? namedGroup(String name) {
|
||||
final groupIndex = groupNames.indexOf(name);
|
||||
if (groupIndex < 0) {
|
||||
throw ArgumentError.value(name, "name", "Not a capture group name");
|
||||
}
|
||||
|
||||
final contents = embedder.regexpMatchGetGroupByName(
|
||||
_match,
|
||||
WasmI32.fromInt(groupIndex),
|
||||
);
|
||||
return contents.isNull ? null : JSStringImpl.fromRefUnchecked(contents);
|
||||
}
|
||||
}
|
||||
|
||||
class _EmbedderMatchesIterator implements Iterator<RegExpMatch> {
|
||||
final EmbedderRegExp _regExp;
|
||||
String? _string;
|
||||
int _nextIndex;
|
||||
RegExpMatch? _current;
|
||||
|
||||
_EmbedderMatchesIterator(this._regExp, this._string, this._nextIndex);
|
||||
|
||||
RegExpMatch get current => _current as RegExpMatch;
|
||||
|
||||
static bool _isLeadSurrogate(int c) {
|
||||
return c >= 0xd800 && c <= 0xdbff;
|
||||
}
|
||||
|
||||
static bool _isTrailSurrogate(int c) {
|
||||
return c >= 0xdc00 && c <= 0xdfff;
|
||||
}
|
||||
|
||||
bool moveNext() {
|
||||
var string = _string;
|
||||
if (string == null) return false;
|
||||
|
||||
if (_nextIndex <= string.length) {
|
||||
final match = _regExp._search(_string!, _nextIndex, false);
|
||||
if (match != null) {
|
||||
_current = match;
|
||||
int nextIndex = match.end;
|
||||
if (match.start == nextIndex) {
|
||||
// Zero-width match. Advance by one more, unless the regexp
|
||||
// is in unicode mode and it would put us within a surrogate
|
||||
// pair. In that case, advance past the code point as a whole.
|
||||
if (_regExp.isUnicode &&
|
||||
_nextIndex + 1 < string.length &&
|
||||
_isLeadSurrogate(string.codeUnitAt(_nextIndex)) &&
|
||||
_isTrailSurrogate(string.codeUnitAt(_nextIndex + 1))) {
|
||||
nextIndex++;
|
||||
}
|
||||
nextIndex++;
|
||||
}
|
||||
_nextIndex = nextIndex;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
_current = null;
|
||||
_string = null; // Marks iteration as ended.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,6 @@
|
||||
import 'dart:_embedder';
|
||||
import 'dart:_error_utils';
|
||||
import 'dart:_internal' show patch;
|
||||
import 'dart:_js_helper';
|
||||
import 'dart:_string';
|
||||
import 'dart:_wasm';
|
||||
|
||||
@@ -19,220 +18,13 @@ class RegExp {
|
||||
bool unicode = false,
|
||||
bool dotAll = false,
|
||||
}) {
|
||||
return _EmbedderRegExp(source, multiLine, caseSensitive, unicode, dotAll);
|
||||
return EmbedderRegExp(source, multiLine, caseSensitive, unicode, dotAll);
|
||||
}
|
||||
|
||||
@patch
|
||||
static String escape(String text) {
|
||||
return JSStringImpl.fromRefUnchecked(
|
||||
regexpEscape(jsStringFromDartString(text).wrappedExternRef),
|
||||
regexpEscape(embedderStringFromDartString(text).wrappedExternRef),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final class _EmbedderRegExp implements RegExp {
|
||||
WasmExternRef? _regexp = WasmExternRef.nullRef;
|
||||
|
||||
@override
|
||||
final String pattern;
|
||||
@override
|
||||
final bool isMultiLine;
|
||||
@override
|
||||
final bool isCaseSensitive;
|
||||
@override
|
||||
final bool isUnicode;
|
||||
@override
|
||||
final bool isDotAll;
|
||||
|
||||
_EmbedderRegExp(
|
||||
this.pattern,
|
||||
this.isMultiLine,
|
||||
this.isCaseSensitive,
|
||||
this.isUnicode,
|
||||
this.isDotAll,
|
||||
) {
|
||||
final compiled = regexpCreateOrFailWithString(
|
||||
jsStringFromDartString(pattern).wrappedExternRef,
|
||||
WasmI32.fromBool(isMultiLine),
|
||||
WasmI32.fromBool(isCaseSensitive),
|
||||
WasmI32.fromBool(isUnicode),
|
||||
WasmI32.fromBool(isDotAll),
|
||||
);
|
||||
if (!regexpIsRegexp(compiled).toBool()) {
|
||||
// The returned value is the stringified JavaScript exception. Turn it
|
||||
// into a Dart exception.
|
||||
final errorMessage = JSStringImpl.fromRefUnchecked(compiled);
|
||||
throw FormatException('Illegal RegExp pattern ($errorMessage)', pattern);
|
||||
}
|
||||
|
||||
this._regexp = compiled;
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
final buffer = StringBuffer('RegExp/');
|
||||
buffer.write(pattern);
|
||||
buffer.write('/');
|
||||
|
||||
if (isMultiLine) buffer.write('m');
|
||||
if (!isCaseSensitive) buffer.write('i');
|
||||
if (isUnicode) buffer.write('u');
|
||||
if (isDotAll) buffer.write('s');
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
@override
|
||||
Iterable<RegExpMatch> allMatches(String input, [int start = 0]) {
|
||||
RangeErrorUtils.checkValueBetweenZeroAndPositiveMax(start, input.length);
|
||||
return Iterable.withIterator(
|
||||
() => _EmbedderMatchesIterator(this, input, start),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
RegExpMatch? firstMatch(String input) {
|
||||
return _search(input, 0, false);
|
||||
}
|
||||
|
||||
@override
|
||||
bool hasMatch(String input) {
|
||||
return firstMatch(input) != null;
|
||||
}
|
||||
|
||||
@override
|
||||
Match? matchAsPrefix(String string, [int start = 0]) {
|
||||
return _search(string, start, true);
|
||||
}
|
||||
|
||||
_EmbedderMatch? _search(String string, int start, bool exactStartIndex) {
|
||||
RangeErrorUtils.checkValueBetweenZeroAndPositiveMax(start, string.length);
|
||||
final match = regexpMatch(
|
||||
_regexp,
|
||||
jsStringFromDartString(string).wrappedExternRef,
|
||||
WasmI32.fromInt(start),
|
||||
WasmI32.fromBool(exactStartIndex),
|
||||
);
|
||||
if (match.isNull) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return _EmbedderMatch(this, string).._match = match;
|
||||
}
|
||||
|
||||
@override
|
||||
String? stringMatch(String input) {
|
||||
var match = firstMatch(input);
|
||||
if (match != null) return match[0];
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
final class _EmbedderMatch implements RegExpMatch {
|
||||
@override
|
||||
final _EmbedderRegExp pattern;
|
||||
@override
|
||||
final String input;
|
||||
|
||||
WasmExternRef? _match = WasmExternRef.nullRef;
|
||||
|
||||
_EmbedderMatch(this.pattern, this.input);
|
||||
|
||||
@override
|
||||
String? operator [](int group) {
|
||||
return this.group(group);
|
||||
}
|
||||
|
||||
@override
|
||||
int get start => regexpMatchGetStart(_match).toIntUnsigned();
|
||||
|
||||
@override
|
||||
int get end => regexpMatchGetEnd(_match).toIntUnsigned();
|
||||
|
||||
@override
|
||||
int get groupCount => regexpMatchGetGroupCount(_match).toIntUnsigned();
|
||||
|
||||
@override
|
||||
String? group(int group) {
|
||||
IndexErrorUtils.checkIndex(group, groupCount + 1);
|
||||
final contents = regexpMatchGetGroup(_match, WasmI32.fromInt(group));
|
||||
return contents.isNull ? null : JSStringImpl.fromRefUnchecked(contents);
|
||||
}
|
||||
|
||||
@override
|
||||
List<String?> groups(List<int> groupIndices) {
|
||||
return [for (final index in groupIndices) group(index)];
|
||||
}
|
||||
|
||||
@override
|
||||
late final List<String> groupNames = List.generate(
|
||||
regexpMatchGetNamedGroups(_match).toIntUnsigned(),
|
||||
(i) {
|
||||
return JSStringImpl.fromRefUnchecked(
|
||||
regexpMatchGetGroupName(_match, WasmI32.fromInt(i)),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
@override
|
||||
String? namedGroup(String name) {
|
||||
final groupIndex = groupNames.indexOf(name);
|
||||
if (groupIndex < 0) {
|
||||
throw ArgumentError.value(name, "name", "Not a capture group name");
|
||||
}
|
||||
|
||||
final contents = regexpMatchGetGroupByName(
|
||||
_match,
|
||||
WasmI32.fromInt(groupIndex),
|
||||
);
|
||||
return contents.isNull ? null : JSStringImpl.fromRefUnchecked(contents);
|
||||
}
|
||||
}
|
||||
|
||||
class _EmbedderMatchesIterator implements Iterator<RegExpMatch> {
|
||||
final _EmbedderRegExp _regExp;
|
||||
String? _string;
|
||||
int _nextIndex;
|
||||
RegExpMatch? _current;
|
||||
|
||||
_EmbedderMatchesIterator(this._regExp, this._string, this._nextIndex);
|
||||
|
||||
RegExpMatch get current => _current as RegExpMatch;
|
||||
|
||||
static bool _isLeadSurrogate(int c) {
|
||||
return c >= 0xd800 && c <= 0xdbff;
|
||||
}
|
||||
|
||||
static bool _isTrailSurrogate(int c) {
|
||||
return c >= 0xdc00 && c <= 0xdfff;
|
||||
}
|
||||
|
||||
bool moveNext() {
|
||||
var string = _string;
|
||||
if (string == null) return false;
|
||||
|
||||
if (_nextIndex <= string.length) {
|
||||
final match = _regExp._search(_string!, _nextIndex, false);
|
||||
if (match != null) {
|
||||
_current = match;
|
||||
int nextIndex = match.end;
|
||||
if (match.start == nextIndex) {
|
||||
// Zero-width match. Advance by one more, unless the regexp
|
||||
// is in unicode mode and it would put us within a surrogate
|
||||
// pair. In that case, advance past the code point as a whole.
|
||||
if (_regExp.isUnicode &&
|
||||
_nextIndex + 1 < string.length &&
|
||||
_isLeadSurrogate(string.codeUnitAt(_nextIndex)) &&
|
||||
_isTrailSurrogate(string.codeUnitAt(_nextIndex + 1))) {
|
||||
nextIndex++;
|
||||
}
|
||||
nextIndex++;
|
||||
}
|
||||
_nextIndex = nextIndex;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
_current = null;
|
||||
_string = null; // Marks iteration as ended.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import 'dart:_embedder';
|
||||
import 'dart:_error_utils' show RangeErrorUtils;
|
||||
import 'dart:_internal' show patch;
|
||||
import 'dart:_js_helper';
|
||||
import 'dart:_string';
|
||||
import 'dart:_wasm';
|
||||
|
||||
@@ -35,6 +35,7 @@ class StringBuffer {
|
||||
|
||||
@patch
|
||||
void writeCharCode(int charCode) {
|
||||
RangeErrorUtils.checkValueBetweenZeroAndPositiveMax(charCode, 0x10ffff);
|
||||
stringBufferWriteCharCode(_hostBuffer, WasmI32.fromInt(charCode));
|
||||
}
|
||||
|
||||
@@ -75,7 +76,7 @@ class StringBuffer {
|
||||
void _writeString(String str) {
|
||||
stringBufferWriteString(
|
||||
_hostBuffer,
|
||||
jsStringFromDartString(str).wrappedExternRef,
|
||||
embedderStringFromDartString(str).wrappedExternRef,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
// Copyright (c) 2026, 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 'dart:_boxed_int';
|
||||
import 'dart:_embedder' show stringFromCharCodeArray;
|
||||
import 'dart:_error_utils';
|
||||
import 'dart:_internal' show EfficientLengthIterable, patch, unsafeCast;
|
||||
import 'dart:_typed_data';
|
||||
import 'dart:_list';
|
||||
import 'dart:_string';
|
||||
import 'dart:_wasm';
|
||||
import 'dart:typed_data';
|
||||
|
||||
@pragma('wasm:initialize-at-startup')
|
||||
const int _stringFromCharCodesSize = 512;
|
||||
final _stringFromCharCodes = WasmArray<WasmI16>(_stringFromCharCodesSize);
|
||||
|
||||
@patch
|
||||
class String {
|
||||
@patch
|
||||
factory String.fromCharCodes(
|
||||
Iterable<int> charCodes, [
|
||||
int start = 0,
|
||||
int? end,
|
||||
]) {
|
||||
RangeError.checkNotNegative(start, "start");
|
||||
if (end != null && end < start) {
|
||||
throw RangeError.range(end, start, null, "end");
|
||||
}
|
||||
if (charCodes is U8List) {
|
||||
return _fromU8ListCharCodes(charCodes, start, end);
|
||||
}
|
||||
if (charCodes is U16List) {
|
||||
return _fromU16ListCharCodes(charCodes, start, end);
|
||||
}
|
||||
if (charCodes is WasmListBase) {
|
||||
final result = _fromWasmListBaseCharCodes(
|
||||
unsafeCast<WasmListBase<int>>(charCodes),
|
||||
start,
|
||||
end,
|
||||
);
|
||||
if (result != null) return result;
|
||||
}
|
||||
return _fromIterableCharCodes(charCodes, start, end);
|
||||
}
|
||||
|
||||
static String _fromU8ListCharCodes(
|
||||
U8List charCodes,
|
||||
int start,
|
||||
int? optionalEnd,
|
||||
) {
|
||||
final length = charCodes.length;
|
||||
int end = optionalEnd != null
|
||||
? (optionalEnd < length ? optionalEnd : length)
|
||||
: length;
|
||||
if (end <= start) return '';
|
||||
|
||||
return JSStringImpl.fromAsciiBytes(
|
||||
charCodes.data,
|
||||
charCodes.offsetInElements + start,
|
||||
charCodes.offsetInElements + end,
|
||||
);
|
||||
}
|
||||
|
||||
static String _fromU16ListCharCodes(
|
||||
U16List charCodes,
|
||||
int start,
|
||||
int? optionalEnd,
|
||||
) {
|
||||
final length = charCodes.length;
|
||||
int end = optionalEnd != null
|
||||
? (optionalEnd < length ? optionalEnd : length)
|
||||
: length;
|
||||
if (end <= start) return '';
|
||||
final count = end - start;
|
||||
|
||||
final int offset = charCodes.offsetInElements;
|
||||
start += offset;
|
||||
end += offset;
|
||||
|
||||
final data = charCodes.data;
|
||||
return JSStringImpl.fromCharCodeArray(data, start, end);
|
||||
}
|
||||
|
||||
static String? _fromWasmListBaseCharCodes(
|
||||
WasmListBase<int> charCodes,
|
||||
int start,
|
||||
int? optionalEnd,
|
||||
) {
|
||||
final length = charCodes.length;
|
||||
final int end = optionalEnd != null
|
||||
? (optionalEnd < length ? optionalEnd : length)
|
||||
: length;
|
||||
if (end <= start) return '';
|
||||
final count = end - start;
|
||||
|
||||
final src = charCodes.data;
|
||||
final dst = count < _stringFromCharCodesSize
|
||||
? _stringFromCharCodes
|
||||
: WasmArray<WasmI16>(count);
|
||||
for (int i = 0; i < count; ++i) {
|
||||
final charCode = unsafeCast<BoxedInt>(src[start + i]);
|
||||
if (charCode.gtU(0xffff)) {
|
||||
return null; // fall back to general case.
|
||||
}
|
||||
dst.write(i, charCode);
|
||||
}
|
||||
return JSStringImpl.fromCharCodeArray(dst, 0, count);
|
||||
}
|
||||
|
||||
static String _fromIterableCharCodes(
|
||||
Iterable<int> charCodes,
|
||||
int start,
|
||||
int? end,
|
||||
) {
|
||||
RangeError.checkNotNegative(start, "start");
|
||||
if (end != null) {
|
||||
if (end < start) {
|
||||
throw RangeError.range(end, start, null, "end");
|
||||
}
|
||||
if (end == start) return "";
|
||||
}
|
||||
|
||||
final length = charCodes.length;
|
||||
|
||||
// Skip until `start`.
|
||||
final it = charCodes.iterator;
|
||||
for (int i = 0; i < start; i++) {
|
||||
it.moveNext();
|
||||
}
|
||||
|
||||
// Convert to WasmArray for JSStringImpl.fromCharCodeArray.
|
||||
final charCodesLength = (end ?? length) - start;
|
||||
if (charCodesLength <= 0) return "";
|
||||
final typedArrayLength = charCodesLength * 2;
|
||||
final WasmArray<WasmI16> list = WasmArray(typedArrayLength);
|
||||
int index = 0; // index in `list`.
|
||||
end ??= start + charCodesLength;
|
||||
for (int i = start; i < end; i++) {
|
||||
if (!it.moveNext()) {
|
||||
break;
|
||||
}
|
||||
final charCode = it.current;
|
||||
if (charCode.leU(0xffff)) {
|
||||
list.write(index++, charCode);
|
||||
} else if (charCode.leU(0x10ffff)) {
|
||||
list.write(index++, 0xd800 + ((((charCode - 0x10000) >> 10) & 0x3ff)));
|
||||
list.write(index++, 0xdc00 + (charCode & 0x3ff));
|
||||
} else {
|
||||
throw RangeError.range(charCode, 0, 0x10ffff);
|
||||
}
|
||||
}
|
||||
|
||||
return JSStringImpl.fromCharCodeArray(list, 0, index);
|
||||
}
|
||||
|
||||
@patch
|
||||
@pragma("wasm:prefer-inline")
|
||||
factory String.fromCharCode(int charCode) {
|
||||
RangeErrorUtils.checkValueBetweenZeroAndPositiveMax(charCode, 0x10ffff);
|
||||
return JSStringImpl.fromCodePoint(charCode);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// Copyright (c) 2026, 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 "dart:_internal" show patch;
|
||||
import "dart:_wasm";
|
||||
import "dart:js_interop";
|
||||
|
||||
@patch
|
||||
extension WasmExternRefToJSAny on WasmExternRef {
|
||||
@patch
|
||||
JSAny get toJS => throw UnsupportedError(
|
||||
'WasmExternRefToJSAny.toJS is unsupported on the standalone target.',
|
||||
);
|
||||
}
|
||||
|
||||
@patch
|
||||
WasmExternRef? externRefForJSAny(JSAny object) => throw UnsupportedError(
|
||||
'externRefForJSAny is unsupported on the standalone target.',
|
||||
);
|
||||
+21
-23
@@ -295,7 +295,7 @@
|
||||
"_internal/wasm_standalone/lib/regexp_patch.dart",
|
||||
"_internal/wasm_standalone/lib/stack_trace_patch.dart",
|
||||
"_internal/wasm_standalone/lib/string_buffer_patch.dart",
|
||||
"_internal/wasm/lib/string_patch.dart",
|
||||
"_internal/wasm_standalone/lib/string_patch.dart",
|
||||
"_internal/wasm_standalone/lib/stopwatch_patch.dart",
|
||||
"_internal/wasm/lib/sync_star_patch.dart",
|
||||
"_internal/wasm_standalone/lib/uri_patch.dart",
|
||||
@@ -334,7 +334,7 @@
|
||||
]
|
||||
},
|
||||
"_string": {
|
||||
"uri": "_internal/wasm/lib/js_string.dart"
|
||||
"uri": "_internal/wasm_standalone/lib/embedder_string.dart"
|
||||
},
|
||||
"_typed_data": {
|
||||
"uri": "_internal/wasm/lib/typed_data.dart",
|
||||
@@ -342,12 +342,6 @@
|
||||
"_internal/wasm_standalone/lib/typed_data_copy_from_js.dart"
|
||||
]
|
||||
},
|
||||
"_js_helper": {
|
||||
"uri": "_internal/wasm/lib/js_helper.dart",
|
||||
"patches": [
|
||||
"_internal/wasm/lib/js_helper_patch.dart"
|
||||
]
|
||||
},
|
||||
"_internal": {
|
||||
"uri": "internal/internal.dart",
|
||||
"patches": [
|
||||
@@ -361,7 +355,7 @@
|
||||
},
|
||||
"_wasm": {
|
||||
"uri": "_wasm/wasm_types.dart",
|
||||
"patches": "_internal/wasm/lib/wasm_types_patch.dart"
|
||||
"patches": "_internal/wasm_standalone/lib/wasm_types_patch.dart"
|
||||
},
|
||||
"math": {
|
||||
"uri": "math/math.dart",
|
||||
@@ -369,6 +363,10 @@
|
||||
"_internal/wasm/lib/math_patch.dart",
|
||||
"_internal/wasm_standalone/lib/math_externs_patch.dart"
|
||||
]
|
||||
},
|
||||
"js_interop": {
|
||||
"uri": "_internal/wasm_standalone/lib/js_interop.dart",
|
||||
"support_conditional_import": false
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -419,6 +417,20 @@
|
||||
"_internal/wasm/lib/math_patch.dart",
|
||||
"_internal/wasm/lib/math_externs_patch.dart"
|
||||
]
|
||||
},
|
||||
"_js_string_convert": {
|
||||
"uri": "_internal/wasm/lib/js_string_convert.dart"
|
||||
},
|
||||
"_js_types": {
|
||||
"uri": "_internal/wasm/lib/js_types.dart"
|
||||
},
|
||||
"js_interop": {
|
||||
"uri": "js_interop/js_interop.dart",
|
||||
"patches": "_internal/wasm/lib/js_interop_patch.dart"
|
||||
},
|
||||
"js_interop_unsafe": {
|
||||
"uri": "js_interop_unsafe/js_interop_unsafe.dart",
|
||||
"patches": "_internal/wasm/lib/js_interop_unsafe_patch.dart"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -448,12 +460,6 @@
|
||||
"_internal/js_shared/lib/http_patch.dart"
|
||||
]
|
||||
},
|
||||
"_js_string_convert": {
|
||||
"uri": "_internal/wasm/lib/js_string_convert.dart"
|
||||
},
|
||||
"_js_types": {
|
||||
"uri": "_internal/wasm/lib/js_types.dart"
|
||||
},
|
||||
"_list": {
|
||||
"uri": "_internal/wasm/lib/list.dart"
|
||||
},
|
||||
@@ -497,14 +503,6 @@
|
||||
"patches": [
|
||||
"_internal/wasm/lib/isolate_patch.dart"
|
||||
]
|
||||
},
|
||||
"js_interop": {
|
||||
"uri": "js_interop/js_interop.dart",
|
||||
"patches": "_internal/wasm/lib/js_interop_patch.dart"
|
||||
},
|
||||
"js_interop_unsafe": {
|
||||
"uri": "js_interop_unsafe/js_interop_unsafe.dart",
|
||||
"patches": "_internal/wasm/lib/js_interop_unsafe_patch.dart"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
+18
-17
@@ -253,7 +253,7 @@ wasm_standalone:
|
||||
- _internal/wasm_standalone/lib/regexp_patch.dart
|
||||
- _internal/wasm_standalone/lib/stack_trace_patch.dart
|
||||
- _internal/wasm_standalone/lib/string_buffer_patch.dart
|
||||
- _internal/wasm/lib/string_patch.dart # TODO(53884): Rewrite without JS interop in _internal/wasm_standalone
|
||||
- _internal/wasm_standalone/lib/string_patch.dart
|
||||
- _internal/wasm_standalone/lib/stopwatch_patch.dart
|
||||
- _internal/wasm/lib/sync_star_patch.dart
|
||||
- _internal/wasm_standalone/lib/uri_patch.dart
|
||||
@@ -282,15 +282,11 @@ wasm_standalone:
|
||||
patches:
|
||||
- _internal/wasm_standalone/lib/boxed_double_patch.dart
|
||||
_string:
|
||||
uri: _internal/wasm/lib/js_string.dart # TODO(53884): Rewrite without JS interop in _internal/wasm_standalone
|
||||
uri: _internal/wasm_standalone/lib/embedder_string.dart
|
||||
_typed_data:
|
||||
uri: _internal/wasm/lib/typed_data.dart
|
||||
patches:
|
||||
- _internal/wasm_standalone/lib/typed_data_copy_from_js.dart
|
||||
_js_helper: # TODO(53884): Remove once other wasm_standalone patches no longer reference this
|
||||
uri: _internal/wasm/lib/js_helper.dart
|
||||
patches:
|
||||
- _internal/wasm/lib/js_helper_patch.dart
|
||||
_internal:
|
||||
uri: internal/internal.dart
|
||||
patches:
|
||||
@@ -302,12 +298,17 @@ wasm_standalone:
|
||||
- _internal/vm_shared/lib/check_valid_weak_target_patch.dart
|
||||
_wasm:
|
||||
uri: _wasm/wasm_types.dart
|
||||
patches: _internal/wasm/lib/wasm_types_patch.dart # TODO(53884): Remove once other wasm_standalone patches no longer reference this
|
||||
patches: _internal/wasm_standalone/lib/wasm_types_patch.dart
|
||||
math:
|
||||
uri: math/math.dart
|
||||
patches:
|
||||
- _internal/wasm/lib/math_patch.dart
|
||||
- _internal/wasm_standalone/lib/math_externs_patch.dart
|
||||
js_interop:
|
||||
# TODO(63166): Remove js_interop from wasm_standalone. It's fundamentally unsupported, but
|
||||
# currently added as a stub because APIs in dart:_wasm reference it.
|
||||
uri: _internal/wasm_standalone/lib/js_interop.dart
|
||||
support_conditional_import: false
|
||||
|
||||
wasm_js_common:
|
||||
include:
|
||||
@@ -343,6 +344,16 @@ wasm_js_common:
|
||||
patches:
|
||||
- _internal/wasm/lib/math_patch.dart
|
||||
- _internal/wasm/lib/math_externs_patch.dart
|
||||
_js_string_convert:
|
||||
uri: _internal/wasm/lib/js_string_convert.dart
|
||||
_js_types:
|
||||
uri: _internal/wasm/lib/js_types.dart
|
||||
js_interop:
|
||||
uri: js_interop/js_interop.dart
|
||||
patches: _internal/wasm/lib/js_interop_patch.dart
|
||||
js_interop_unsafe:
|
||||
uri: js_interop_unsafe/js_interop_unsafe.dart
|
||||
patches: _internal/wasm/lib/js_interop_unsafe_patch.dart
|
||||
|
||||
wasm_common:
|
||||
libraries:
|
||||
@@ -362,10 +373,6 @@ wasm_common:
|
||||
uri: _http/http.dart
|
||||
patches:
|
||||
- _internal/js_shared/lib/http_patch.dart
|
||||
_js_string_convert:
|
||||
uri: _internal/wasm/lib/js_string_convert.dart
|
||||
_js_types:
|
||||
uri: _internal/wasm/lib/js_types.dart
|
||||
_list:
|
||||
uri: _internal/wasm/lib/list.dart
|
||||
_object_helper:
|
||||
@@ -398,12 +405,6 @@ wasm_common:
|
||||
uri: isolate/isolate.dart
|
||||
patches:
|
||||
- "_internal/wasm/lib/isolate_patch.dart"
|
||||
js_interop:
|
||||
uri: js_interop/js_interop.dart
|
||||
patches: _internal/wasm/lib/js_interop_patch.dart
|
||||
js_interop_unsafe:
|
||||
uri: js_interop_unsafe/js_interop_unsafe.dart
|
||||
patches: _internal/wasm/lib/js_interop_unsafe_patch.dart
|
||||
|
||||
dart2js:
|
||||
include:
|
||||
|
||||
@@ -49,3 +49,8 @@ LibTest/js_interop/importModule_A02_t01: Skip # https://github.com/dart-lang/sdk
|
||||
LibTest/js_interop/importModule_A02_t02: Skip # https://github.com/dart-lang/sdk/issues/61204
|
||||
LibTest/js_interop/importModule_A02_t03: Skip # https://github.com/dart-lang/sdk/issues/61204
|
||||
LibTest/js_interop/interop_A08_t05: Skip # https://github.com/dart-lang/sdk/issues/61204
|
||||
|
||||
[ $compiler == dart2wasm && $dart2wasm_standalone ]
|
||||
LanguageFeatures/Augmentations/js_interop/*: SkipByDesign # This is a generic WebAssembly target without JavaScript support
|
||||
LibTest/js_interop/*: SkipByDesign # This is a generic WebAssembly target without JavaScript support
|
||||
LibTest/js_interop_unsafe/*: SkipByDesign # This is a generic WebAssembly target without JavaScript support
|
||||
|
||||
@@ -37,9 +37,13 @@ main() {
|
||||
const bool.fromEnvironment("dart.library.web_gl"),
|
||||
);
|
||||
|
||||
// All web backends support `dart:js_interop`
|
||||
// All web backends support `dart:js_interop`. dart2wasm standalone is tested
|
||||
// on the web but is not a web backend.
|
||||
final isDart2WasmStandalone = const String.fromEnvironment(
|
||||
"test_runner.configuration",
|
||||
).contains('standalone');
|
||||
Expect.equals(
|
||||
isWebConfiguration,
|
||||
isWebConfiguration && !isDart2WasmStandalone,
|
||||
const bool.fromEnvironment("dart.library.js_interop"),
|
||||
);
|
||||
|
||||
|
||||
@@ -135,6 +135,10 @@ js_interop_unsafe/*: SkipByDesign # Only supported on web backends.
|
||||
[ $compiler != dart2js && $compiler != ddc ]
|
||||
web/*: SkipByDesign
|
||||
|
||||
[ $compiler == dart2wasm && $dart2wasm_standalone ]
|
||||
js/*: SkipByDesign # This is a generic WebAssembly target without JavaScript support
|
||||
js_interop_unsafe/*: SkipByDesign # This is a generic WebAssembly target without JavaScript support
|
||||
|
||||
[ $runtime == chrome && $system == macos ]
|
||||
convert/streamed_conversion_utf8_encode_test: SkipSlow # Times out. Issue 22050
|
||||
html/canvasrendering/arc_test: Skip # Issue 42048
|
||||
|
||||
Reference in New Issue
Block a user