Skip to content

Commit 9ed368c

Browse files
osa1joshualitt
authored and
Commit Queue
committed
[dart2wasm] Use only JS strings in JSCM
Refactor libraries so that JSCM will only use `JSStringImpl` class for strings. The goal is to disentangle the native string classes and `JSStringImpl` and start testing `JSStringImpl` in isolation. Changes: - `dart:_string` is no longer available in JSCM. - Make `int.toString` external to allow patching it differently in JSCM and normal modes. `toString` implementations are in `boxed_int_to_string.dart` patch files. - `int.parse` now uses JS `parseInt`. However `parseInt` is not compatible with Dart's `int.parse` so this will cause some more test failures in JSCM for now. - Any dependencies to `dart:_string` from JSCM `dart:convert` are removed. The library implementation now uses JS `TextDecoder` for UTF-8 decoding. Note: `TextDecoder` is not available on d8, so text decoding tests will fail on d8. JSON encoding and decoding in `dart:convert` will be updated in a follow-up CL. - Compiler (translator, constant generator, code generator etc.) is updated to allocate the `JSStringImpl`s in JSCM. Initially this will make some JSCM test fail as `int` parsing is not quite right, those will be fixed in follow-up CLs. Co-authored-by: Joshua Litt <[email protected]> Change-Id: I366e06f44cdc369d28fe47b24015234260304399 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/332680 Reviewed-by: Aske Simon Christensen <[email protected]> Commit-Queue: Ömer Ağacan <[email protected]>
1 parent acad657 commit 9ed368c

26 files changed

+921
-909
lines changed

pkg/dart2wasm/lib/class_info.dart

+9-4
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ class FieldIndex {
5454
static const syncStarIteratorCurrent = 3;
5555
static const syncStarIteratorYieldStarIterable = 4;
5656
static const recordFieldBase = 2;
57+
static const jsStringImplRef = 2;
5758

5859
static void validate(Translator translator) {
5960
void check(Class cls, String name, int expectedIndex) {
@@ -82,8 +83,10 @@ class FieldIndex {
8283
check(translator.boxedBoolClass, "value", FieldIndex.boxValue);
8384
check(translator.boxedIntClass, "value", FieldIndex.boxValue);
8485
check(translator.boxedDoubleClass, "value", FieldIndex.boxValue);
85-
check(translator.oneByteStringClass, "_array", FieldIndex.stringArray);
86-
check(translator.twoByteStringClass, "_array", FieldIndex.stringArray);
86+
if (!translator.options.jsCompatibility) {
87+
check(translator.oneByteStringClass, "_array", FieldIndex.stringArray);
88+
check(translator.twoByteStringClass, "_array", FieldIndex.stringArray);
89+
}
8790
check(translator.listBaseClass, "_length", FieldIndex.listLength);
8891
check(translator.listBaseClass, "_data", FieldIndex.listArray);
8992
check(translator.hashFieldBaseClass, "_indexNullable",
@@ -259,6 +262,7 @@ class ClassInfoCollector {
259262
/// These types switch from properly reified non-masquerading types in regular
260263
/// Dart2Wasm mode to masquerading types in js compatibility mode.
261264
final Set<String> jsCompatibilityTypes = {
265+
"JSStringImpl",
262266
"JSArrayBufferImpl",
263267
"JSArrayBufferViewImpl",
264268
"JSDataViewImpl",
@@ -284,7 +288,6 @@ class ClassInfoCollector {
284288
final jsTypesLibraryIndex =
285289
LibraryIndex(translator.component, ["dart:_js_types"]);
286290
final neverMasquerades = [
287-
"JSStringImpl",
288291
if (!translator.options.jsCompatibility) ...jsCompatibilityTypes,
289292
]
290293
.map((name) => jsTypesLibraryIndex.tryGetClass("dart:_js_types", name))
@@ -344,7 +347,9 @@ class ClassInfoCollector {
344347
ClassInfo superInfo = cls == translator.coreTypes.boolClass ||
345348
cls == translator.coreTypes.numClass
346349
? topInfo
347-
: cls == translator.stringBaseClass || cls == translator.typeClass
350+
: (!translator.options.jsCompatibility &&
351+
cls == translator.stringBaseClass) ||
352+
cls == translator.typeClass
348353
? translator.classInfo[cls.implementedTypes.single.classNode]!
349354
: translator.classInfo[superclass]!;
350355

pkg/dart2wasm/lib/code_generator.dart

+6-3
Original file line numberDiff line numberDiff line change
@@ -2837,8 +2837,9 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
28372837
node.expressions,
28382838
InterfaceType(
28392839
translator.coreTypes.stringClass, Nullability.nonNullable));
2840-
return translator
2841-
.outputOrVoid(call(translator.stringInterpolate.reference));
2840+
return translator.outputOrVoid(call(translator.options.jsCompatibility
2841+
? translator.jsStringInterpolate.reference
2842+
: translator.stringInterpolate.reference));
28422843
}
28432844

28442845
@override
@@ -3711,7 +3712,9 @@ class SwitchInfo {
37113712
.classInfo[translator.coreTypes.stringClass]!.repr.nonNullableType;
37123713
nullableType = translator
37133714
.classInfo[translator.coreTypes.stringClass]!.repr.nullableType;
3714-
compare = () => codeGen.call(translator.stringEquals.reference);
3715+
compare = () => codeGen.call(translator.options.jsCompatibility
3716+
? translator.jsStringEquals.reference
3717+
: translator.stringEquals.reference);
37153718
} else {
37163719
// Object switch
37173720
nonNullableType = translator.topInfo.nonNullableType;

pkg/dart2wasm/lib/compile.dart

+4-2
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,9 @@ Future<CompilerOutput?> compileToModule(compiler.CompilerOptions options,
151151
}
152152

153153
final wasmModule = translator.translate();
154-
String jsRuntime =
155-
jsRuntimeFinalizer.generate(translator.functions.translatedProcedures);
154+
String jsRuntime = jsRuntimeFinalizer.generate(
155+
translator.functions.translatedProcedures,
156+
translator.internalizedStringsForJSRuntime,
157+
mode);
156158
return CompilerOutput(wasmModule, jsRuntime);
157159
}

pkg/dart2wasm/lib/constants.dart

+11-2
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,15 @@ class ConstantCreator extends ConstantVisitor<ConstantInfo?>
322322

323323
@override
324324
ConstantInfo? visitStringConstant(StringConstant constant) {
325+
if (translator.options.jsCompatibility) {
326+
ClassInfo info = translator.classInfo[translator.jsStringClass]!;
327+
return createConstant(constant, info.nonNullableType, (function, b) {
328+
b.i32_const(info.classId);
329+
b.i32_const(initialIdentityHash);
330+
b.global_get(translator.getInternalizedStringGlobal(constant.value));
331+
b.struct_new(info.struct);
332+
});
333+
}
325334
bool isOneByte = constant.value.codeUnits.every((c) => c <= 255);
326335
ClassInfo info = translator.classInfo[isOneByte
327336
? translator.oneByteStringClass
@@ -885,8 +894,8 @@ class ConstantCreator extends ConstantVisitor<ConstantInfo?>
885894
ConstantInfo? visitSymbolConstant(SymbolConstant constant) {
886895
ClassInfo info = translator.classInfo[translator.symbolClass]!;
887896
translator.functions.allocateClass(info.classId);
888-
w.RefType stringType =
889-
translator.classInfo[translator.coreTypes.stringClass]!.nonNullableType;
897+
w.RefType stringType = translator
898+
.classInfo[translator.coreTypes.stringClass]!.repr.nonNullableType;
890899
StringConstant nameConstant = StringConstant(constant.name);
891900
bool lazy = ensureConstant(nameConstant)?.isLazy ?? false;
892901
return createConstant(constant, info.nonNullableType, lazy: lazy,

pkg/dart2wasm/lib/js/runtime_blob.dart

+22-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ let buildArgsList;
1414
// This function returns a promise to the instantiated module.
1515
export const instantiate = async (modulePromise, importObjectPromise) => {
1616
let dartInstance;
17+
''';
18+
19+
// Break to support system dependent conversion routines.
20+
const jsRuntimeBlobPart2Regular = r'''
1721
function stringFromDartString(string) {
1822
const totalLength = dartInstance.exports.$stringLength(string);
1923
let result = '';
@@ -49,7 +53,20 @@ export const instantiate = async (modulePromise, importObjectPromise) => {
4953
return dartString;
5054
}
5155
}
56+
''';
57+
58+
// Conversion functions for JSCM.
59+
const jsRuntimeBlobPart2JSCM = r'''
60+
function stringFromDartString(string) {
61+
return dartInstance.exports.$jsStringFromJSStringImpl(string);
62+
}
5263
64+
function stringToDartString(string) {
65+
return dartInstance.exports.$jsStringToJSStringImpl(string);
66+
}
67+
''';
68+
69+
const jsRuntimeBlobPart3 = r'''
5370
// Converts a Dart List to a JS array. Any Dart objects will be converted, but
5471
// this will be cheap for JSValues.
5572
function arrayFromDartList(constructor, list) {
@@ -84,11 +101,15 @@ export const instantiate = async (modulePromise, importObjectPromise) => {
84101

85102
// We break inside the 'dart2wasm' object to enable injection of methods. We
86103
// could use interpolation, but then we'd have to escape characters.
87-
const jsRuntimeBlobPart2 = r'''
104+
const jsRuntimeBlobPart4 = r'''
88105
};
89106
90107
const baseImports = {
91108
dart2wasm: dart2wasm,
109+
''';
110+
111+
// We break inside of `baseImports` to inject internalized strings.
112+
const jsRuntimeBlobPart5 = r'''
92113
Math: Math,
93114
Date: Date,
94115
Object: Object,

pkg/dart2wasm/lib/js/runtime_generator.dart

+20-2
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,13 @@ import 'package:_js_interop_checks/src/transformations/static_interop_class_eras
88
import 'package:dart2wasm/js/interop_transformer.dart';
99
import 'package:dart2wasm/js/method_collector.dart';
1010
import 'package:dart2wasm/js/runtime_blob.dart';
11+
import 'package:dart2wasm/target.dart' as wasm_target;
1112
import 'package:kernel/ast.dart';
1213
import 'package:kernel/class_hierarchy.dart';
1314
import 'package:kernel/core_types.dart';
1415

16+
import 'dart:convert' show json;
17+
1518
JSMethods _performJSInteropTransformations(
1619
Component component,
1720
CoreTypes coreTypes,
@@ -46,18 +49,33 @@ class RuntimeFinalizer {
4649

4750
RuntimeFinalizer(this.allJSMethods);
4851

49-
String generate(Iterable<Procedure> translatedProcedures) {
52+
String generate(Iterable<Procedure> translatedProcedures,
53+
List<String> constantStrings, wasm_target.Mode mode) {
54+
String escape(String s) => json.encode(s);
55+
5056
Set<Procedure> usedProcedures = {};
5157
List<String> usedJSMethods = [];
5258
for (Procedure p in translatedProcedures) {
5359
if (usedProcedures.add(p) && allJSMethods.containsKey(p)) {
5460
usedJSMethods.add(allJSMethods[p]!);
5561
}
5662
}
63+
64+
String internalizedStrings = '';
65+
if (constantStrings.isNotEmpty) {
66+
internalizedStrings = '''
67+
s: [
68+
${constantStrings.map(escape).join(',\n')}
69+
],''';
70+
}
5771
return '''
5872
$jsRuntimeBlobPart1
73+
${mode == wasm_target.Mode.jsCompatibility ? jsRuntimeBlobPart2JSCM : jsRuntimeBlobPart2Regular}
74+
$jsRuntimeBlobPart3
5975
${usedJSMethods.join(',\n')}
60-
$jsRuntimeBlobPart2
76+
$jsRuntimeBlobPart4
77+
$internalizedStrings
78+
$jsRuntimeBlobPart5
6179
''';
6280
}
6381
}

pkg/dart2wasm/lib/kernel_nodes.dart

+20-2
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,24 @@ mixin KernelNodes {
1212

1313
late final LibraryIndex index = LibraryIndex(component, [
1414
"dart:_internal",
15+
"dart:_js_helper",
16+
"dart:_js_types",
17+
"dart:_string",
18+
"dart:_wasm",
1519
"dart:async",
1620
"dart:collection",
1721
"dart:core",
1822
"dart:ffi",
1923
"dart:typed_data",
20-
"dart:_string",
21-
"dart:_wasm",
2224
]);
2325

2426
// dart:_internal classes
2527
late final Class symbolClass = index.getClass("dart:_internal", "Symbol");
2628

29+
// dart:_js_types classes
30+
late final Class jsStringClass =
31+
index.getClass("dart:_js_types", "JSStringImpl");
32+
2733
// dart:collection classes
2834
late final Class hashFieldBaseClass =
2935
index.getClass("dart:collection", "_HashFieldBase");
@@ -144,6 +150,18 @@ mixin KernelNodes {
144150
late final Procedure checkLibraryIsLoaded =
145151
index.getTopLevelProcedure("dart:_internal", "checkLibraryIsLoaded");
146152

153+
// dart:_js_helper procedures
154+
late final Procedure getInternalizedString =
155+
index.getTopLevelProcedure("dart:_js_helper", "getInternalizedString");
156+
late final Procedure areEqualInJS =
157+
index.getTopLevelProcedure("dart:_js_helper", "areEqualInJS");
158+
159+
// dart:_js_types procedures
160+
late final Procedure jsStringEquals =
161+
index.getProcedure("dart:_js_types", "JSStringImpl", "==");
162+
late final Procedure jsStringInterpolate =
163+
index.getProcedure("dart:_js_types", "JSStringImpl", "interpolate");
164+
147165
// dart:collection procedures
148166
late final Procedure mapFactory =
149167
index.getProcedure("dart:collection", "LinkedHashMap", "_default");

pkg/dart2wasm/lib/target.dart

+10-4
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ class WasmTarget extends Target {
102102
Class? _wasmImmutableSet;
103103
Class? _oneByteString;
104104
Class? _twoByteString;
105+
Class? _jsString;
105106
Class? _closure;
106107
Map<String, Class>? _nativeClasses;
107108

@@ -141,12 +142,11 @@ class WasmTarget extends Target {
141142
TargetFlags get flags => TargetFlags();
142143

143144
@override
144-
List<String> get extraRequiredLibraries => const <String>[
145+
List<String> get extraRequiredLibraries => [
145146
'dart:_http',
146147
'dart:_internal',
147148
'dart:_js_helper',
148149
'dart:_js_types',
149-
'dart:_string',
150150
'dart:_wasm',
151151
'dart:async',
152152
'dart:developer',
@@ -158,19 +158,20 @@ class WasmTarget extends Target {
158158
'dart:js_util',
159159
'dart:nativewrappers',
160160
'dart:typed_data',
161+
if (mode != Mode.jsCompatibility) 'dart:_string',
161162
];
162163

163164
@override
164-
List<String> get extraIndexedLibraries => const <String>[
165+
List<String> get extraIndexedLibraries => [
165166
'dart:_js_helper',
166167
'dart:_js_types',
167-
'dart:_string',
168168
'dart:_wasm',
169169
'dart:collection',
170170
'dart:js_interop',
171171
'dart:js_interop_unsafe',
172172
'dart:js_util',
173173
'dart:typed_data',
174+
if (mode != Mode.jsCompatibility) 'dart:_string',
174175
];
175176

176177
@override
@@ -462,6 +463,11 @@ class WasmTarget extends Target {
462463

463464
@override
464465
Class concreteStringLiteralClass(CoreTypes coreTypes, String value) {
466+
// In JSCM all strings are JS strings.
467+
if (mode == Mode.jsCompatibility) {
468+
return _jsString ??=
469+
coreTypes.index.getClass("dart:_js_types", "JSStringImpl");
470+
}
465471
const int maxLatin1 = 0xff;
466472
for (int i = 0; i < value.length; ++i) {
467473
if (value.codeUnitAt(i) > maxLatin1) {

pkg/dart2wasm/lib/translator.dart

+22-2
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,10 @@ class Translator with KernelNodes {
8484
/// [ClassInfoCollector].
8585
final Map<Class, ClassInfo> classInfo = {};
8686

87+
/// Internalized strings to move to the JS runtime
88+
final List<String> internalizedStringsForJSRuntime = [];
89+
final Map<String, w.Global> _internalizedStringGlobals = {};
90+
8791
final Map<w.HeapType, ClassInfo> classForHeapType = {};
8892
final Map<Field, int> fieldIndex = {};
8993
final Map<TypeParameter, int> typeParameterIndex = {};
@@ -161,8 +165,11 @@ class Translator with KernelNodes {
161165
boxedIntClass: boxedIntClass,
162166
boxedDoubleClass: boxedDoubleClass,
163167
boxedBoolClass: coreTypes.boolClass,
164-
oneByteStringClass: stringBaseClass,
165-
twoByteStringClass: stringBaseClass,
168+
if (!options.jsCompatibility) ...{
169+
oneByteStringClass: stringBaseClass,
170+
twoByteStringClass: stringBaseClass
171+
},
172+
if (options.jsCompatibility) ...{jsStringClass: jsStringClass},
166173
};
167174

168175
/// Type for vtable entries for dynamic calls. These entries are used in
@@ -1026,6 +1033,19 @@ class Translator with KernelNodes {
10261033

10271034
ClassInfo getRecordClassInfo(RecordType recordType) =>
10281035
classInfo[recordClasses[RecordShape.fromType(recordType)]!]!;
1036+
1037+
w.Global getInternalizedStringGlobal(String s) {
1038+
w.Global? internalizedString = _internalizedStringGlobals[s];
1039+
if (internalizedString != null) {
1040+
return internalizedString;
1041+
}
1042+
final i = internalizedStringsForJSRuntime.length;
1043+
internalizedString = m.globals.import('s', '$i',
1044+
w.GlobalType(w.RefType.extern(nullable: true), mutable: false));
1045+
_internalizedStringGlobals[s] = internalizedString;
1046+
internalizedStringsForJSRuntime.add(s);
1047+
return internalizedString;
1048+
}
10291049
}
10301050

10311051
abstract class _FunctionGenerator {

0 commit comments

Comments
 (0)