From 34a6cbc03b09766c8d6fcbbd22faf65a14d7aa08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Sinan=20A=C4=9Facan?= Date: Wed, 13 Nov 2024 10:38:30 +0000 Subject: [PATCH] [dart2wasm] Use a Wasm array directly in chunked JSON parser state stack MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Commit-Queue: Ömer Ağacan --- pkg/dart2wasm/lib/transformers.dart | 26 ++++++++++++------- sdk/lib/_internal/wasm/lib/convert_patch.dart | 19 +++++++++++--- .../_internal/wasm/lib/internal_patch.dart | 6 ++++- 3 files changed, 36 insertions(+), 15 deletions(-) diff --git a/pkg/dart2wasm/lib/transformers.dart b/pkg/dart2wasm/lib/transformers.dart index 1cb91bee027e..d5edd23630e3 100644 --- a/pkg/dart2wasm/lib/transformers.dart +++ b/pkg/dart2wasm/lib/transformers.dart @@ -962,8 +962,8 @@ class PushPopWasmArrayTransformer { 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 @@ class PushPopWasmArrayTransformer { return cloner.clone(node); } + final List blockStatements = []; + // length - 1 final intSubtractType = _intSubtract.computeSignatureOrFunctionType(); final lengthMinusOne = InstanceInvocation(InstanceAccessKind.Instance, @@ -1008,24 +1010,28 @@ class PushPopWasmArrayTransformer { 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 270bc6b5496d..eff0229acf77 100644 --- a/sdk/lib/_internal/wasm/lib/convert_patch.dart +++ b/sdk/lib/_internal/wasm/lib/convert_patch.dart @@ -120,7 +120,7 @@ class _JsonListener { GrowableList? stackPop() { assert(stackLength != 0); - return popWasmArray>(stack, stackLength); + return popWasmArray?>(stack, stackLength); } /** Contents of the current container being built, or null if not building a @@ -328,7 +328,9 @@ abstract class _ChunkedJsonParserState { // The current parsing state. int state = _ChunkedJsonParser.STATE_INITIAL; - GrowableList states = GrowableList.empty(); + + WasmArray states = WasmArray(0); + int statesLength = 0; /** * Stores tokenizer state between chunks. @@ -390,6 +392,7 @@ abstract class _ChunkedJsonParserState { ) { state = chunkedParserState.state; states = chunkedParserState.states; + statesLength = chunkedParserState.statesLength; partialState = chunkedParserState.partialState; _stringBuffer = chunkedParserState._stringBuffer; _numberBuffer = chunkedParserState._numberBuffer; @@ -402,13 +405,21 @@ abstract class _ChunkedJsonParserState { * so the parser can go back to the correct value when the literal ends. */ void saveState(int state) { - states.add(state); + pushWasmArray( + 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(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 4186b79c7903..859dc6b344df 100644 --- a/sdk/lib/_internal/wasm/lib/internal_patch.dart +++ b/sdk/lib/_internal/wasm/lib/internal_patch.dart @@ -226,4 +226,8 @@ external void pushWasmArray( ); /// Similar to `pushWasmArray`, but for popping. -external T? popWasmArray(WasmArray 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(WasmArray array, int length);