[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>
This commit is contained in:
Stephen Adams 2020-12-11 01:29:28 +00:00 committed by commit-bot@chromium.org
parent 60ec210866
commit cc3c70159d
7 changed files with 83 additions and 36 deletions

View file

@ -449,6 +449,8 @@ abstract class CommonElements {
FunctionEntity getInstantiateFunction(int typeArgumentCount);
FunctionEntity get convertMainArgumentList;
// From dart:_rti
FunctionEntity get setRuntimeTypeInfo;
@ -1834,6 +1836,10 @@ class CommonElementsImpl
cls.name.startsWith('Instantiation');
}
@override
FunctionEntity get convertMainArgumentList =>
_findHelperFunction('convertMainArgumentList');
// From dart:_rti
ClassEntity _findRtiClass(String name) => _findClass(rtiLibrary, name);

View file

@ -117,10 +117,13 @@ class BackendImpacts {
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;

View file

@ -9,16 +9,58 @@ import 'package:js_runtime/shared/embedded_names.dart' as embeddedNames;
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 @@ class MainCallStubGenerator {
}
})(function(currentScript) {
#currentScript = currentScript;
var callMain = #mainCallClosure;
if (typeof dartMainRunner === "function") {
dartMainRunner(#mainCallClosure, []);
dartMainRunner(callMain, []);
} else {
#mainCallClosure([]);
callMain([]);
}
})''', {
'currentScript': currentScriptAccess,

View file

@ -388,7 +388,7 @@ class ProgramBuilder {
js.Statement _buildInvokeMain() {
return MainCallStubGenerator.generateInvokeMain(
_task.emitter, _mainFunction);
_commonElements, _task.emitter, _mainFunction);
}
DeferredFragment _buildDeferredFragment(LibrariesMap librariesMap) {

View file

@ -2960,24 +2960,20 @@ Future<Null> _loadHunk(String hunkName) {
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 {

View file

@ -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 @@ if (typeof global != "undefined") self = global; // Node.js.
// 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 @@ if (typeof global != "undefined") self = global; // Node.js.
// so pretend they don't exist.
// TODO(30217): Try to use D8's worker.
delete self.Worker;
})(self);
})(self, arguments);

View file

@ -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 = [];