[dart2js] Convert first argument to `main`
- When `main` has arguments, use a callMain shim to convert first
argument passed to `main` to a `List<String>`.
- Make js_runtime/preambles/{d8,jsshell}.js pass command line arguments to
Dart `main`.
Change-Id: I8c04bbe49366e756cfe84d1c705767e0366ee74c
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/158373
Reviewed-by: Sigmund Cherem <sigmund@google.com>
Commit-Queue: Stephen Adams <sra@google.com>
diff --git a/pkg/compiler/lib/src/common_elements.dart b/pkg/compiler/lib/src/common_elements.dart
index 8d84fe7..0cb4190 100644
--- a/pkg/compiler/lib/src/common_elements.dart
+++ b/pkg/compiler/lib/src/common_elements.dart
@@ -449,6 +449,8 @@
FunctionEntity getInstantiateFunction(int typeArgumentCount);
+ FunctionEntity get convertMainArgumentList;
+
// From dart:_rti
FunctionEntity get setRuntimeTypeInfo;
@@ -1834,6 +1836,10 @@
cls.name.startsWith('Instantiation');
}
+ @override
+ FunctionEntity get convertMainArgumentList =>
+ _findHelperFunction('convertMainArgumentList');
+
// From dart:_rti
ClassEntity _findRtiClass(String name) => _findClass(rtiLibrary, name);
diff --git a/pkg/compiler/lib/src/js_backend/backend_impact.dart b/pkg/compiler/lib/src/js_backend/backend_impact.dart
index 5f63cfb..39c5e4f 100644
--- a/pkg/compiler/lib/src/js_backend/backend_impact.dart
+++ b/pkg/compiler/lib/src/js_backend/backend_impact.dart
@@ -117,10 +117,13 @@
BackendImpact _mainWithArguments;
BackendImpact get mainWithArguments {
- return _mainWithArguments ??= new BackendImpact(instantiatedClasses: [
- _commonElements.jsArrayClass,
- _commonElements.jsStringClass
- ]);
+ return _mainWithArguments ??= new BackendImpact(
+ globalUses: [_commonElements.convertMainArgumentList],
+ instantiatedClasses: [
+ _commonElements.jsArrayClass,
+ _commonElements.jsStringClass
+ ],
+ );
}
BackendImpact _asyncBody;
diff --git a/pkg/compiler/lib/src/js_emitter/main_call_stub_generator.dart b/pkg/compiler/lib/src/js_emitter/main_call_stub_generator.dart
index e20f889..d26bb2e 100644
--- a/pkg/compiler/lib/src/js_emitter/main_call_stub_generator.dart
+++ b/pkg/compiler/lib/src/js_emitter/main_call_stub_generator.dart
@@ -9,16 +9,58 @@
import '../elements/entities.dart';
import '../js/js.dart' as jsAst;
import '../js/js.dart' show js;
+import '../common_elements.dart';
import 'code_emitter_task.dart' show Emitter;
class MainCallStubGenerator {
static jsAst.Statement generateInvokeMain(
- Emitter emitter, FunctionEntity main) {
- jsAst.Expression mainCallClosure = emitter.staticFunctionAccess(main);
+ CommonElements commonElements, Emitter emitter, FunctionEntity main) {
+ jsAst.Expression mainAccess = emitter.staticFunctionAccess(main);
jsAst.Expression currentScriptAccess =
emitter.generateEmbeddedGlobalAccess(embeddedNames.CURRENT_SCRIPT);
+ // TODO(https://github.com/dart-lang/language/issues/1120#issuecomment-670802088):
+ // Validate constraints on `main()` in resolution for dart2js, and in DDC.
+
+ final parameterStructure = main.parameterStructure;
+
+ // The forwarding stub passes all arguments, i.e. both required and optional
+ // positional arguments. We ignore named arguments, assuming the `main()`
+ // has been validated earlier.
+ int positionalParameters = parameterStructure.positionalParameters;
+
+ jsAst.Expression mainCallClosure;
+ if (positionalParameters == 0) {
+ if (parameterStructure.namedParameters.isEmpty) {
+ // e.g. `void main()`.
+ // No parameters. The compiled Dart `main` has no parameters and will
+ // ignore any extra parameters passed in, so it can be used directly.
+ mainCallClosure = mainAccess;
+ } else {
+ // e.g. `void main({arg})`. We should not get here. Drop the named
+ // arguments as we don't know how to convert them.
+ mainCallClosure = js(r'''function() { return #(); }''', mainAccess);
+ }
+ } else {
+ jsAst.Expression convertArgumentList =
+ emitter.staticFunctionAccess(commonElements.convertMainArgumentList);
+ if (positionalParameters == 1) {
+ // e.g. `void main(List<String> args)`, `main([args])`.
+ mainCallClosure = js(
+ r'''function(args) { return #(#(args)); }''',
+ [mainAccess, convertArgumentList],
+ );
+ } else {
+ // positionalParameters == 2.
+ // e.g. `void main(List<String> args, Object? extra)`
+ mainCallClosure = js(
+ r'''function(args, extra) { return #(#(args), extra); }''',
+ [mainAccess, convertArgumentList],
+ );
+ }
+ }
+
// This code finds the currently executing script by listening to the
// onload event of all script tags and getting the first script which
// finishes. Since onload is called immediately after execution this should
@@ -48,11 +90,11 @@
}
})(function(currentScript) {
#currentScript = currentScript;
-
+ var callMain = #mainCallClosure;
if (typeof dartMainRunner === "function") {
- dartMainRunner(#mainCallClosure, []);
+ dartMainRunner(callMain, []);
} else {
- #mainCallClosure([]);
+ callMain([]);
}
})''', {
'currentScript': currentScriptAccess,
diff --git a/pkg/compiler/lib/src/js_emitter/program_builder/program_builder.dart b/pkg/compiler/lib/src/js_emitter/program_builder/program_builder.dart
index 351d6bc..99e7544 100644
--- a/pkg/compiler/lib/src/js_emitter/program_builder/program_builder.dart
+++ b/pkg/compiler/lib/src/js_emitter/program_builder/program_builder.dart
@@ -388,7 +388,7 @@
js.Statement _buildInvokeMain() {
return MainCallStubGenerator.generateInvokeMain(
- _task.emitter, _mainFunction);
+ _commonElements, _task.emitter, _mainFunction);
}
DeferredFragment _buildDeferredFragment(LibrariesMap librariesMap) {
diff --git a/sdk/lib/_internal/js_runtime/lib/js_helper.dart b/sdk/lib/_internal/js_runtime/lib/js_helper.dart
index 3b1cca4..570aa15 100644
--- a/sdk/lib/_internal/js_runtime/lib/js_helper.dart
+++ b/sdk/lib/_internal/js_runtime/lib/js_helper.dart
@@ -2960,24 +2960,20 @@
return completer.future;
}
-class MainError extends Error implements NoSuchMethodError {
- final String _message;
-
- MainError(this._message);
-
- String toString() => 'NoSuchMethodError: $_message';
-}
-
-void missingMain() {
- throw new MainError("No top-level function named 'main'.");
-}
-
-void badMain() {
- throw new MainError("'main' is not a function.");
-}
-
-void mainHasTooManyParameters() {
- throw new MainError("'main' expects too many parameters.");
+/// Converts a raw JavaScript array into a `List<String>`.
+/// Called from generated code.
+List<String> convertMainArgumentList(Object? args) {
+ List<String> result = [];
+ if (args == null) return result;
+ if (args is JSArray) {
+ for (int i = 0; i < args.length; i++) {
+ JS('', '#.push(String(#[#]))', result, args, i);
+ }
+ return result;
+ }
+ // Single non-Array element. Convert to a String.
+ JS('', '#.push(String(#))', result, args);
+ return result;
}
class _AssertionError extends AssertionError {
diff --git a/sdk/lib/_internal/js_runtime/lib/preambles/d8.js b/sdk/lib/_internal/js_runtime/lib/preambles/d8.js
index 0174d95..6604c8a 100644
--- a/sdk/lib/_internal/js_runtime/lib/preambles/d8.js
+++ b/sdk/lib/_internal/js_runtime/lib/preambles/d8.js
@@ -10,7 +10,7 @@
var self = this;
if (typeof global != "undefined") self = global; // Node.js.
-(function(self) {
+(function(self, scriptArguments) {
// Using strict mode to avoid accidentally defining global variables.
"use strict"; // Should be first statement of this function.
@@ -270,9 +270,9 @@
// Global properties. "self" refers to the global object, so adding a
// property to "self" defines a global variable.
self.self = self;
- self.dartMainRunner = function(main, args) {
+ self.dartMainRunner = function(main, ignored_args) {
// Initialize.
- var action = function() { main(args); }
+ var action = function() { main(scriptArguments, null); }
eventLoop(action);
};
self.setTimeout = addTimer;
@@ -345,4 +345,4 @@
// so pretend they don't exist.
// TODO(30217): Try to use D8's worker.
delete self.Worker;
-})(self);
+})(self, arguments);
diff --git a/sdk/lib/_internal/js_runtime/lib/preambles/jsshell.js b/sdk/lib/_internal/js_runtime/lib/preambles/jsshell.js
index a6b41c4..48f60cd 100644
--- a/sdk/lib/_internal/js_runtime/lib/preambles/jsshell.js
+++ b/sdk/lib/_internal/js_runtime/lib/preambles/jsshell.js
@@ -4,7 +4,7 @@
// Javascript preamble, that lets the output of dart2js run on JSShell.
-(function(self) {
+(function(self, scriptArguments) {
// Using strict mode to avoid accidentally defining global variables.
"use strict"; // Should be first statement of this function.
@@ -262,9 +262,9 @@
}
}
- self.dartMainRunner = function(main, args) {
+ self.dartMainRunner = function(main, ignored_args) {
// Initialize.
- var action = function() { main(args); }
+ var action = function() { main(scriptArguments, null); }
eventLoop(action);
};
self.setTimeout = addTimer;
@@ -329,7 +329,7 @@
array[i] = Math.random() * 256;
}
}};
-})(this)
+})(this, typeof scriptArgs == "undefined" ? [] : scriptArgs)
var getKeys = function(obj){
var keys = [];