[dart2wasm] Use a Wasm array directly in chunked JSON parser state stack Update `popWasmArray` desugaring to only clear the popped slot when the element type is nullable. Use `push/popWasmArray` in `_ChunkedJsonParserState.states` stack to avoid indirection. Change-Id: I641b4a78b85640b3676ef935bfd98b8cd5f7789d Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/394483 Reviewed-by: Slava Egorov <vegorov@google.com> Commit-Queue: Ömer Ağacan <omersa@google.com>
diff --git a/pkg/dart2wasm/lib/transformers.dart b/pkg/dart2wasm/lib/transformers.dart index 1cb91be..d5edd23 100644 --- a/pkg/dart2wasm/lib/transformers.dart +++ b/pkg/dart2wasm/lib/transformers.dart
@@ -962,8 +962,8 @@ Expression _transformPopWasmArray(StaticInvocation invocation) { final elementType = invocation.arguments.types[0] as InterfaceType; - final elementTypeNullable = - elementType.withDeclaredNullability(Nullability.nullable); + final elementIsNullable = + elementType.nullability != Nullability.nonNullable; final positionalArguments = invocation.arguments.positional; assert(positionalArguments.length == 2); @@ -991,6 +991,8 @@ return cloner.clone(node); } + final List<Statement> blockStatements = []; + // length - 1 final intSubtractType = _intSubtract.computeSignatureOrFunctionType(); final lengthMinusOne = InstanceInvocation(InstanceAccessKind.Instance, @@ -1008,24 +1010,28 @@ arrayLengthUpdate = ExpressionStatement( VariableSet(lengthVariableGet.variable, lengthMinusOne)); } + blockStatements.add(arrayLengthUpdate); // array[length] final arrayGet = StaticInvocation(_wasmArrayElementGet, - Arguments([clone(array), clone(length)], types: [elementTypeNullable])); + Arguments([clone(array), clone(length)], types: [elementType])); // final temp = array[length] final arrayGetVariable = VariableDeclaration.forValue(arrayGet, - isFinal: true, type: elementTypeNullable); + isFinal: true, type: elementType); + blockStatements.add(arrayGetVariable); // array[length] = null - final arrayClearElement = ExpressionStatement(StaticInvocation( - _wasmArrayElementSet, - Arguments([clone(array), clone(length), NullLiteral()], - types: [elementTypeNullable]))); + if (elementIsNullable) { + final arrayClearElement = ExpressionStatement(StaticInvocation( + _wasmArrayElementSet, + Arguments([clone(array), clone(length), NullLiteral()], + types: [elementType]))); + blockStatements.add(arrayClearElement); + } return BlockExpression( - Block([arrayLengthUpdate, arrayGetVariable, arrayClearElement]), - VariableGet(arrayGetVariable)); + Block(blockStatements), VariableGet(arrayGetVariable)); } }
diff --git a/sdk/lib/_internal/wasm/lib/convert_patch.dart b/sdk/lib/_internal/wasm/lib/convert_patch.dart index 270bc6b..eff0229 100644 --- a/sdk/lib/_internal/wasm/lib/convert_patch.dart +++ b/sdk/lib/_internal/wasm/lib/convert_patch.dart
@@ -120,7 +120,7 @@ GrowableList<dynamic>? stackPop() { assert(stackLength != 0); - return popWasmArray<GrowableList<dynamic>>(stack, stackLength); + return popWasmArray<GrowableList<dynamic>?>(stack, stackLength); } /** Contents of the current container being built, or null if not building a @@ -328,7 +328,9 @@ // The current parsing state. int state = _ChunkedJsonParser.STATE_INITIAL; - GrowableList<int> states = GrowableList<int>.empty(); + + WasmArray<WasmI64> states = WasmArray<WasmI64>(0); + int statesLength = 0; /** * Stores tokenizer state between chunks. @@ -390,6 +392,7 @@ ) { state = chunkedParserState.state; states = chunkedParserState.states; + statesLength = chunkedParserState.statesLength; partialState = chunkedParserState.partialState; _stringBuffer = chunkedParserState._stringBuffer; _numberBuffer = chunkedParserState._numberBuffer; @@ -402,13 +405,21 @@ * so the parser can go back to the correct value when the literal ends. */ void saveState(int state) { - states.add(state); + pushWasmArray<WasmI64>( + states, + statesLength, + state.toWasmI64(), + (statesLength * 2) | 3, + ); } /** * Restore a state pushed with [saveState]. */ - int restoreState() => states.removeLast(); // Throws if empty. + int restoreState() { + assert(statesLength > 0); + return popWasmArray<WasmI64>(states, statesLength).toInt(); + } /** * Read out the result after successfully closing the parser.
diff --git a/sdk/lib/_internal/wasm/lib/internal_patch.dart b/sdk/lib/_internal/wasm/lib/internal_patch.dart index 4186b79..859dc6b 100644 --- a/sdk/lib/_internal/wasm/lib/internal_patch.dart +++ b/sdk/lib/_internal/wasm/lib/internal_patch.dart
@@ -226,4 +226,8 @@ ); /// Similar to `pushWasmArray`, but for popping. -external T? popWasmArray<T>(WasmArray<T?> array, int length); +/// +/// Note that when [T] is not nullable, this does not clear the popped element +/// slot in the array, which may cause memory leaks. Callers should manually +/// clear non-nullable reference element slots in the array when popping. +external T popWasmArray<T>(WasmArray<T> array, int length);