Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions packages/dart/example/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,23 @@ Future<void> main() async {
..set('Name', 'Ketogenic')
..set('Fat', 65);



var response = await dietPlan.save();

if (response.success) {
dietPlan = response.results?.first;
print("Response received successfully");
}

final res = await ParseAggregate('DietPlan').execute({
r'$match': {'Name': 'Ketogenic'},
r'$group': {
'_id': r'isHungry',
'count': {r'$sum': 1}
},
});

print('${res.statusCode}, ${res.results}');

}
56 changes: 3 additions & 53 deletions packages/dart/lib/parse_server_sdk.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,111 +29,61 @@ export 'src/network/parse_dio_client.dart';
export 'src/network/parse_http_client.dart';

part 'src/base/parse_constants.dart';

part 'src/data/parse_core_data.dart';

part 'src/data/parse_subclass_handler.dart';

part 'src/enums/parse_enum_api_rq.dart';

part 'src/network/options.dart';

// part 'src/network/parse_aggregate.dart';
part 'src/network/parse_client.dart';

part 'src/network/parse_connectivity.dart';

part 'src/network/parse_live_query.dart';

part 'src/network/parse_query.dart';

part 'src/objects/parse_acl.dart';

part 'src/objects/parse_aggregate.dart';
part 'src/objects/parse_array.dart';

part 'src/objects/parse_base.dart';

part 'src/objects/parse_cloneable.dart';

part 'src/objects/parse_config.dart';

part 'src/objects/parse_error.dart';

part 'src/objects/parse_exception.dart';

part 'src/objects/parse_file.dart';

part 'src/objects/parse_file_base.dart';

part 'src/objects/parse_file_web.dart';

part 'src/objects/parse_function.dart';

part 'src/objects/parse_geo_point.dart';

part 'src/objects/parse_installation.dart';

part 'src/objects/parse_jobs.dart';
part 'src/objects/parse_number.dart';

part 'src/objects/parse_object.dart';

part 'src/objects/parse_operation/parse_add_operation.dart';

part 'src/objects/parse_operation/parse_add_relation_operation.dart';

part 'src/objects/parse_operation/parse_add_unique_operation.dart';

part 'src/objects/parse_operation/parse_increment_operation.dart';

part 'src/objects/parse_operation/parse_operation.dart';

part 'src/objects/parse_operation/parse_remove_operation.dart';

part 'src/objects/parse_operation/parse_remove_relation_operation.dart';

part 'src/objects/parse_relation.dart';

part 'src/objects/parse_response.dart';

part 'src/objects/parse_save_state_aware_child.dart';

part 'src/objects/parse_session.dart';

part 'src/objects/parse_user.dart';

part 'src/objects/parse_x_file.dart';

part 'src/objects/response/parse_error_response.dart';

part 'src/objects/response/parse_exception_response.dart';

part 'src/objects/response/parse_response_builder.dart';

part 'src/objects/response/parse_response_utils.dart';

part 'src/objects/response/parse_success_no_results.dart';

part 'src/storage/core_store.dart';

part 'src/storage/core_store_memory.dart';

part 'src/storage/core_store_sem_impl.dart';

part 'src/storage/xxtea_codec.dart';

part 'src/utils/parse_date_format.dart';

part 'src/utils/parse_decoder.dart';

part 'src/utils/parse_encoder.dart';

part 'src/utils/parse_live_list.dart';

part 'src/utils/parse_logger.dart';

part 'src/utils/parse_login_helpers.dart';

part 'src/utils/parse_utils.dart';

part 'src/utils/valuable.dart';

class Parse {
Expand Down
2 changes: 2 additions & 0 deletions packages/dart/lib/src/base/parse_constants.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ const String keyEndPointRequestPasswordReset = '/requestPasswordReset';
const String keyEndPointClasses = '/classes/';
const String keyEndPointHealth = '/health';
const String keyEndPointAggregate = '/aggregate/';
const String keyEndPointFunctions = '/functions/';
const String keyEndPointJobs = '/jobs/';

// ParseObject variables
const String keyVarClassName = 'className';
Expand Down
4 changes: 2 additions & 2 deletions packages/dart/lib/src/network/parse_http_client.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import 'dart:convert';
import 'package:universal_io/io.dart';

import 'package:http/http.dart' as http;

import 'package:parse_server_sdk/parse_server_sdk.dart';
import 'package:universal_io/io.dart';

import 'http_client_io.dart' if (dart.library.js) 'http_client_js.dart';

Expand Down Expand Up @@ -31,6 +30,7 @@ class ParseHTTPClient extends ParseClient {
ParseNetworkOptions? options,
ProgressCallback? onReceiveProgress,
}) async {

final http.Response response = await _client.get(
Uri.parse(path),
headers: options?.headers,
Expand Down
41 changes: 33 additions & 8 deletions packages/dart/lib/src/network/parse_websocket_html.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
/// If you change this file, you should apply the same changes to the 'parse_websocket_io.dart' file
library;

import 'dart:html' as html;

import 'dart:async';

import 'package:socket_io_client/socket_io_client.dart' as IO;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

socket_io_client dart package latest version 2025 security

💡 Result:

  • Latest stable socket_io_client (Dart) on pub.dev: 3.1.3 (published recently). [1]

  • Known/security notes:

    • No public CVE listing specifically for the Dart socket_io_client package was found; pub.dev shows current package metadata but does not list vulnerabilities. Still check the package repo/issues before use. [1]
    • There are known Socket.IO ecosystem vulnerabilities you should consider when using any client/server combination: CVE-2024-38355 (uncaught error packets can crash Node.js Socket.IO servers; upgrade server-side socket.io to fixed versions). [2]
    • Separate advisory for python-socketio (CVE-2025-61765) describes RCE via pickle deserialization in multi-server setups using an untrusted message queue — an example that server-side implementations can introduce platform-specific risks. [3]
  • Practical recommendations:

    • Use socket_io_client 3.1.3 and keep it updated. [1]
    • Keep your Socket.IO server implementation patched to the latest secure release (follow the server project's SECURITY.md). [2]
    • Use TLS (wss), authenticate/authorize connections, validate and sandbox incoming messages, and avoid trusting inter-server message queues (or use safe serialization). [2][3]
    • Monitor the package repo and vulnerability databases (GitHub Advisories, Snyk, CVE feeds) for new issues.

Sources:
[1] pub.dev — socket_io_client package page and versions.
[2] CVE-2024-38355 / Snyk / CVE details (Socket.IO uncaught error crash).
[3] GitHub Advisory / CVE-2025-61765 (python-socketio pickle deserialization advisory).


🌐 Web query:

socket_io_client pub.dev security vulnerabilities advisories

💡 Result:

Known/security advisories affecting Socket.IO clients (summary):

  • CVE-2024-38355 — Unhandled "error" event can crash a Socket.IO server (DoS). Fixes shipped in socket.io 4.6.2 (and backported to 2.x). (wiz.io)
  • CVE-2022-25867 — io.socket:socket.io-client (Java) null-pointer dereference when parsing malformed packets; fixed in socket.io-client 2.0.1. (osv.dev)
  • CVE-2025-61765 — python-socketio: unsafe pickle deserialization in multi‑server setups (RCE) when attacker has access to the message queue; fixed in python-socketio ≥5.14.0. (nvd.nist.gov)

Recommendations: upgrade client/server packages to the patched versions above, validate/limit inputs, avoid trusting untrusted message-queue payloads (or use safe serializers), and monitor the Socket.IO GitHub security advisories for new issues. (github.com)

Citations:


Update socket_io_client to version 3.1.3.

The imported socket_io_client package is at v3.1.2; version 3.1.3 is available and should be used to ensure the latest stability and bug fixes. No public vulnerabilities specific to the Dart package have been identified, though general Socket.IO ecosystem best practices (e.g., keep the server implementation patched) should be followed.

🤖 Prompt for AI Agents
In packages/dart/lib/src/network/parse_websocket_html.dart around line 7, the
project currently pulls socket_io_client v3.1.2; update the dependency to 3.1.3
by changing the socket_io_client entry in pubspec.yaml to "socket_io_client:
^3.1.3" (or exact 3.1.3 as your versioning policy requires), run flutter pub get
/ dart pub get, and verify there are no new import or API changes required by
running the tests or building the package.

import 'package:web_socket_channel/html.dart';
import 'package:web_socket_channel/web_socket_channel.dart';

Expand All @@ -14,18 +16,41 @@ class WebSocket {
static const int closing = 2;
static const int closed = 3;

final html.WebSocket _webSocket;
final IO.Socket _webSocket;

static final Map<String, int> _states={
'closed':3,
'opening':0,
'open':1,
'closing':2
};
static Future<WebSocket> connect(String liveQueryURL) async {
final html.WebSocket webSocket = html.WebSocket(liveQueryURL);
await webSocket.onOpen.first;
return WebSocket._(webSocket);
Completer<WebSocket> completer= Completer();
final IO.Socket webSocket = IO.io(
liveQueryURL,
IO.OptionBuilder().setTransports(['websocket']).enableReconnection().build()
);
webSocket.connect();
webSocket.onConnect((handler){
if(!completer.isCompleted){
completer.complete(WebSocket._(webSocket));
}
});
webSocket.onConnectError((handler){
if(!completer.isCompleted){
completer.completeError('unable to connect to the server $handler');
}
});



return completer.future;
}
Comment on lines 27 to 48
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Address multiple issues in the connect method.

Several improvements needed:

  1. Line 28: Missing space before the = operator (style inconsistency).
  2. Line 41: The error handler completes with a string instead of a proper Exception or Error type.
  3. No timeout: If neither onConnect nor onConnectError fires, the returned future will hang indefinitely. Consider adding a timeout.
  4. Lines 45-46: Unnecessary blank lines before the return statement.
🔎 Proposed improvements
  static Future<WebSocket> connect(String liveQueryURL) async {
-    Completer<WebSocket> completer= Completer();
+    final Completer<WebSocket> completer = Completer();
    final IO.Socket webSocket = IO.io(
      liveQueryURL,
      IO.OptionBuilder().setTransports(['websocket']).enableReconnection().build()
    );
    webSocket.connect();
-    webSocket.onConnect((handler){
-      if(!completer.isCompleted){
+    webSocket.onConnect((handler) {
+      if (!completer.isCompleted) {
        completer.complete(WebSocket._(webSocket));
      }
    });
-    webSocket.onConnectError((handler){
-      if(!completer.isCompleted){
-        completer.completeError('unable to connect to the server $handler');
+    webSocket.onConnectError((handler) {
+      if (!completer.isCompleted) {
+        completer.completeError(
+          Exception('Unable to connect to the server: $handler')
+        );
      }
    });

-    
-
-    return completer.future;
+    return completer.future.timeout(
+      const Duration(seconds: 30),
+      onTimeout: () {
+        webSocket.disconnect();
+        throw TimeoutException('WebSocket connection timeout');
+      },
+    );
  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
static Future<WebSocket> connect(String liveQueryURL) async {
final html.WebSocket webSocket = html.WebSocket(liveQueryURL);
await webSocket.onOpen.first;
return WebSocket._(webSocket);
Completer<WebSocket> completer= Completer();
final IO.Socket webSocket = IO.io(
liveQueryURL,
IO.OptionBuilder().setTransports(['websocket']).enableReconnection().build()
);
webSocket.connect();
webSocket.onConnect((handler){
if(!completer.isCompleted){
completer.complete(WebSocket._(webSocket));
}
});
webSocket.onConnectError((handler){
if(!completer.isCompleted){
completer.completeError('unable to connect to the server $handler');
}
});
return completer.future;
}
static Future<WebSocket> connect(String liveQueryURL) async {
final Completer<WebSocket> completer = Completer();
final IO.Socket webSocket = IO.io(
liveQueryURL,
IO.OptionBuilder().setTransports(['websocket']).enableReconnection().build()
);
webSocket.connect();
webSocket.onConnect((handler) {
if (!completer.isCompleted) {
completer.complete(WebSocket._(webSocket));
}
});
webSocket.onConnectError((handler) {
if (!completer.isCompleted) {
completer.completeError(
Exception('Unable to connect to the server: $handler')
);
}
});
return completer.future.timeout(
const Duration(seconds: 30),
onTimeout: () {
webSocket.disconnect();
throw TimeoutException('WebSocket connection timeout');
},
);
}
🤖 Prompt for AI Agents
In packages/dart/lib/src/network/parse_websocket_html.dart around lines 27 to
48, fix style, error handling and hanging-future issues: add a space before the
'=' at the Completer declaration to match style; change completer.completeError
to pass a proper Exception (e.g., throw or completeError(SocketException or
Exception) with the handler message) instead of a raw string; add a timeout
wrapper (or schedule a timer) that completes the completer with a
TimeoutException if neither onConnect nor onConnectError fire within a
reasonable period (and cancel the timer on success/error); and remove the extra
blank lines before the return statement so the method returns completer.future
immediately after handlers are set up.


int get readyState => _webSocket.readyState;
int get readyState => _states[_webSocket.io.readyState]!;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

cat -n packages/dart/lib/src/network/parse_websocket_html.dart | head -100

Repository: parse-community/Parse-SDK-Flutter

Length of output: 1982


🏁 Script executed:

rg "_states\s*=" packages/dart/lib/src/network/parse_websocket_html.dart -A 10 -B 2

Repository: parse-community/Parse-SDK-Flutter

Length of output: 472


🏁 Script executed:

rg "readyState" packages/dart/lib/src/network/parse_websocket_html.dart -B 5 -A 5

Repository: parse-community/Parse-SDK-Flutter

Length of output: 254


🏁 Script executed:

cat -n packages/dart/lib/src/network/parse_websocket_io.dart | head -100

Repository: parse-community/Parse-SDK-Flutter

Length of output: 1097


🏁 Script executed:

rg "readyState" packages/dart/lib/src/network/parse_websocket_io.dart -B 5 -A 5

Repository: parse-community/Parse-SDK-Flutter

Length of output: 321


🏁 Script executed:

fd pubspec.yaml packages/dart | xargs cat | grep -A 5 "socket_io_client"

Repository: parse-community/Parse-SDK-Flutter

Length of output: 166


🏁 Script executed:

rg "io\.readyState" packages/dart -B 3 -A 3

Repository: parse-community/Parse-SDK-Flutter

Length of output: 635


🏁 Script executed:

rg "readyState" packages/dart --type dart -B 2 -A 2

Repository: parse-community/Parse-SDK-Flutter

Length of output: 2388


🏁 Script executed:

fd "pubspec.lock" packages/dart -exec cat {} \; | grep -A 20 "socket_io_client"

Repository: parse-community/Parse-SDK-Flutter

Length of output: 223


🏁 Script executed:

find packages/dart -name "pubspec.lock" -type f -exec head -200 {} \; | grep -A 30 "socket_io_client:"

Repository: parse-community/Parse-SDK-Flutter

Length of output: 59


🏁 Script executed:

rg "socket_io_client" packages/dart --type yaml

Repository: parse-community/Parse-SDK-Flutter

Length of output: 130


🌐 Web query:

socket_io_client dart v3.1.2 readyState possible values

💡 Result:

The readyState values follow Dart/WebSocket constants:

  • 0 = connecting
  • 1 = open (connected)
  • 2 = closing
  • 3 = closed. [1][2]

Sources: socket_io_client (pub.dev v3.1.2) and Dart WebSocket readyState/connecting constant. [1] https://pub.dev/packages/socket_io_client/versions/3.1.2/example [2] https://api.dart.dev/stable/dart-io/WebSocket/connecting-constant.html


🌐 Web query:

socket_io_client dart readyState states documentation

💡 Result:

Short answer — readyState uses the standard WebSocket integer states:

  • 0 = CONNECTING
  • 1 = OPEN (connected)
  • 2 = CLOSING
  • 3 = CLOSED

These are the WebSocket readyState values used by dart:io / dart:html (and therefore what socket_io_client will reflect for the underlying socket). See Dart WebSocket readyState and the socket_io_client package docs. [1][2][3]

References:

  • Dart WebSocket readyState (dart:io / dart:html). [1]
  • socket_io_client package documentation (events and connection behavior). [2]
  • MDN WebSocket.readyState (WebSocket spec mapping 0–3). [3]

[1] https://api.dart.dev/dart-io/WebSocket/readyState.html
[2] https://pub.dev/packages/socket_io_client
[3] https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/readyState


Remove the string-to-int state mapping; io.readyState already returns the integer state constant directly.

The _states map uses string keys, but _webSocket.io.readyState returns an integer (0, 1, 2, or 3 per the WebSocket standard used by socket_io_client v3.1.2). The map lookup will always fail, causing the force unwrap to crash on every access. This contradicts the IO implementation (parse_websocket_io.dart line 23), which correctly returns _webSocket.readyState without mapping.

🔎 Recommended fix
-  int get readyState => _states[_webSocket.io.readyState]!;
+  int get readyState => _webSocket.io.readyState;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
int get readyState => _states[_webSocket.io.readyState]!;
int get readyState => _webSocket.io.readyState;
🤖 Prompt for AI Agents
In packages/dart/lib/src/network/parse_websocket_html.dart around line 50, the
code looks up _webSocket.io.readyState in a string-keyed _states map which fails
because readyState is already an integer; replace the map lookup with a direct
return of the integer state (i.e., return _webSocket.io.readyState) and remove
or stop using the _states mapping so the getter returns the raw integer state
like parse_websocket_io.dart does.


Future<void> close() async {
return _webSocket.close();
Future<IO.Socket> close() async {
return _webSocket.disconnect();
}
Comment on lines +52 to 54
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Breaking API change: close() return type modified.

The return type changed from Future<void> to Future<IO.Socket>, which is a breaking change for existing callers. Unless there's a specific reason to return the disconnected socket, consider maintaining the original signature for backward compatibility.

🔎 Suggested fix to maintain backward compatibility
-  Future<IO.Socket> close() async {
-    return _webSocket.disconnect();
+  Future<void> close() async {
+    _webSocket.disconnect();
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Future<IO.Socket> close() async {
return _webSocket.disconnect();
}
Future<void> close() async {
_webSocket.disconnect();
}
🤖 Prompt for AI Agents
In packages/dart/lib/src/network/parse_websocket_html.dart around lines 52 to
54, the close() method's signature was changed to return Future<IO.Socket> which
is a breaking API change; revert the method to return Future<void> (or
overload/introduce a new method if you need to expose the socket) and ensure the
implementation still awaits _webSocket.disconnect() but does not return the
socket. Update any internal calls if they relied on the socket return, or add a
new method (e.g., disconnectAndReturnSocket) to preserve the new behavior
without breaking existing callers.


WebSocketChannel createWebSocketChannel() {
Expand Down
53 changes: 53 additions & 0 deletions packages/dart/lib/src/objects/parse_aggregate.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@

part of '../../parse_server_sdk.dart';

class ParseAggregate extends ParseObject {

ParseAggregate(
this.functionName, {
bool? debug,
ParseClient? client,
bool? autoSendSessionId,
}) : super(
functionName,
client: client,
autoSendSessionId: autoSendSessionId,
debug: debug,
) {
_path = '$keyEndPointAggregate$functionName';
}

final String functionName;

@override
late String _path;

Future<ParseResponse> execute(Map<String, dynamic> pipeline, {Map<String, String>? headers}) async {
final String uri = '${ParseCoreData().serverUrl}$_path';

Map<String, String> parameters = {};

if (pipeline.isEmpty) {
throw ArgumentError(
'pipeline must not be empty. Please add pipeline operations to aggregate data. '
'Example: {"\$group": {"_id": "\$userId", "totalScore": {"\$sum": "\$score"}}}',
);
} else {
parameters.addAll({
'pipeline': jsonEncode(pipeline.entries.map((e) => {e.key: e.value}).toList())
});
_setObjectData(pipeline);
}

try {
print(Uri.parse(uri).replace(queryParameters: parameters).toString());
final ParseNetworkResponse result = await _client.get(
Uri.parse(uri).replace(queryParameters: parameters).toString(),
options: ParseNetworkOptions(headers: headers)
);
return ParseResponse.fromParseNetworkResponse(result);
} on Exception catch (e) {
return handleException(e, ParseApiRQ.execute, _debug, parseClassName);
}
}
}
2 changes: 1 addition & 1 deletion packages/dart/lib/src/objects/parse_function.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class ParseCloudFunction extends ParseObject {
autoSendSessionId: autoSendSessionId,
debug: debug,
) {
_path = '/functions/$functionName';
_path = '$keyEndPointFunctions$functionName';
}

final String functionName;
Expand Down
58 changes: 58 additions & 0 deletions packages/dart/lib/src/objects/parse_jobs.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
part of '../../parse_server_sdk.dart';

class ParseJobs extends ParseObject {
/// Creates a new cloud function object
///
/// {https://docs.parseplatform.org/cloudcode/guide/}
ParseJobs(
this.functionName, {
bool? debug,
ParseClient? client,
bool? autoSendSessionId,
}) : super(
functionName,
client: client,
autoSendSessionId: autoSendSessionId,
debug: debug,
) {
_path = '$keyEndPointJobs$functionName';
}

final String functionName;

@override
// ignore: overridden_fields
late String _path;

/// Executes a cloud function
///
/// To add the parameters, create an object and call [set](value to set)
Future<ParseResponse> execute({Map<String, dynamic>? parameters, Map<String, String>? headers}) async {
final String uri = '${ParseCoreData().serverUrl}$_path';
if (parameters != null) {
_setObjectData(parameters);
}
try {
final ParseNetworkResponse result = await _client.post(uri, options: ParseNetworkOptions(headers: headers), data: json.encode(_getObjectData()));
return handleResponse<ParseJobs>(this, result, ParseApiRQ.execute, _debug, parseClassName);
} on Exception catch (e) {
return handleException(e, ParseApiRQ.execute, _debug, parseClassName);
}
}

/// Executes a cloud function that returns a ParseObject type
///
/// To add the parameters, create an object and call [set](value to set)
Future<ParseResponse> executeObjectFunction<T extends ParseObject>({Map<String, dynamic>? parameters, Map<String, String>? headers}) async {
final String uri = '${ParseCoreData().serverUrl}$_path';
if (parameters != null) {
_setObjectData(parameters);
}
try {
final ParseNetworkResponse result = await _client.post(uri, options: ParseNetworkOptions(headers: headers), data: json.encode(_getObjectData()));
return handleResponse<T>(this, result, ParseApiRQ.executeObjectionFunction, _debug, parseClassName);
} on Exception catch (e) {
return handleException(e, ParseApiRQ.executeObjectionFunction, _debug, parseClassName);
}
}
}
Loading