From 6410051fd0503272bb5cf14aecae82fd99937044 Mon Sep 17 00:00:00 2001 From: Olivier Chafik Date: Tue, 1 Dec 2015 19:02:43 +0000 Subject: [PATCH] Convert dart_utils.js to input_sdk/lib/_internal/utils.dart (#310) BUG= R=jmesserly@google.com, vsm@google.com Review URL: https://codereview.chromium.org/1486473002 . --- pkg/dev_compiler/lib/runtime/dart/_classes.js | 5 +- .../lib/runtime/dart/_operations.js | 3 +- pkg/dev_compiler/lib/runtime/dart/_rtti.js | 3 +- pkg/dev_compiler/lib/runtime/dart/_runtime.js | 5 +- pkg/dev_compiler/lib/runtime/dart/_types.js | 9 +- .../runtime/{dart_utils.js => dart/_utils.js} | 107 ++++++--------- pkg/dev_compiler/lib/runtime/dart_library.js | 11 +- .../lib/src/codegen/js_codegen.dart | 91 +++++++++++-- pkg/dev_compiler/lib/src/compiler.dart | 2 +- .../lib/src/runner/runtime_utils.dart | 2 +- pkg/dev_compiler/pubspec.yaml | 2 +- .../test/browser/runtime_tests.js | 1 + .../collection/src/unmodifiable_wrappers.js | 2 +- .../codegen/expect/collection/wrappers.js | 4 +- .../test/codegen/expect/dir/html_input_b.js | 2 +- .../test/codegen/expect/html_input.html | 2 +- pkg/dev_compiler/test/codegen/expect/js/js.js | 2 +- .../codegen/expect/sunflower/sunflower.html | 2 +- .../test/codegen/expect/unittest.js | 2 +- .../test/codegen/expect/unittest/unittest.js | 2 +- .../input_sdk/lib/_internal/libraries.dart | 6 + .../tool/input_sdk/private/js_primitives.dart | 5 + .../tool/input_sdk/private/utils.dart | 123 ++++++++++++++++++ pkg/dev_compiler/tool/sdk_expected_errors.txt | 6 + 24 files changed, 295 insertions(+), 104 deletions(-) rename pkg/dev_compiler/lib/runtime/{dart_utils.js => dart/_utils.js} (50%) create mode 100644 pkg/dev_compiler/tool/input_sdk/private/utils.dart diff --git a/pkg/dev_compiler/lib/runtime/dart/_classes.js b/pkg/dev_compiler/lib/runtime/dart/_classes.js index 5c2171e20aac..36590277167f 100644 --- a/pkg/dev_compiler/lib/runtime/dart/_classes.js +++ b/pkg/dev_compiler/lib/runtime/dart/_classes.js @@ -12,14 +12,15 @@ // TODO(leafp): Consider splitting some of this out. dart_library.library('dart/_classes', null, /* Imports */[ ], /* Lazy Imports */[ + 'dart/_utils', 'dart/core', 'dart/_interceptors', 'dart/_types', 'dart/_rtti', -], function(exports, core, _interceptors, types, rtti) { +], function(exports, dart_utils, core, _interceptors, types, rtti) { 'use strict'; - const assert = dart_utils.assert; + const assert = dart_utils.assert_; const copyProperties = dart_utils.copyProperties; const copyTheseProperties = dart_utils.copyTheseProperties; const defineMemoizedGetter = dart_utils.defineMemoizedGetter; diff --git a/pkg/dev_compiler/lib/runtime/dart/_operations.js b/pkg/dev_compiler/lib/runtime/dart/_operations.js index edce6cdf8490..fe7b6caa2e3a 100644 --- a/pkg/dev_compiler/lib/runtime/dart/_operations.js +++ b/pkg/dev_compiler/lib/runtime/dart/_operations.js @@ -7,6 +7,7 @@ */ dart_library.library('dart/_operations', null, /* Imports */[ ], /* Lazy Imports */[ + 'dart/_utils', 'dart/async', 'dart/collection', 'dart/core', @@ -15,7 +16,7 @@ dart_library.library('dart/_operations', null, /* Imports */[ 'dart/_errors', 'dart/_rtti', 'dart/_types' -], function(exports, async, collection, core, _js_helper, classes, errors, rtti, +], function(exports, dart_utils, async, collection, core, _js_helper, classes, errors, rtti, types) { 'use strict'; diff --git a/pkg/dev_compiler/lib/runtime/dart/_rtti.js b/pkg/dev_compiler/lib/runtime/dart/_rtti.js index badaf8bdd3f4..168f91e4d984 100644 --- a/pkg/dev_compiler/lib/runtime/dart/_rtti.js +++ b/pkg/dev_compiler/lib/runtime/dart/_rtti.js @@ -8,9 +8,10 @@ dart_library.library('dart/_rtti', null, /* Imports */[ ], /* Lazy Imports */[ + 'dart/_utils', 'dart/core', 'dart/_types' -], function(exports, core, types) { +], function(exports, dart_utils, core, types) { 'use strict'; const defineLazyProperty = dart_utils.defineLazyProperty; diff --git a/pkg/dev_compiler/lib/runtime/dart/_runtime.js b/pkg/dev_compiler/lib/runtime/dart/_runtime.js index 32172bcdb8b6..506049d5e5d8 100644 --- a/pkg/dev_compiler/lib/runtime/dart/_runtime.js +++ b/pkg/dev_compiler/lib/runtime/dart/_runtime.js @@ -3,6 +3,7 @@ // BSD-style license that can be found in the LICENSE file. dart_library.library('dart/_runtime', null, /* Imports */[ + 'dart/_utils', 'dart/_classes', 'dart/_errors', 'dart/_generators', @@ -11,7 +12,7 @@ dart_library.library('dart/_runtime', null, /* Imports */[ 'dart/_types', ], /* Lazy Imports */[ 'dart/_js_helper' -], function(exports, classes, errors, generators, operations, rtti, types, +], function(exports, dart_utils, classes, errors, generators, operations, rtti, types, _js_helper) { 'use strict'; @@ -64,7 +65,7 @@ dart_library.library('dart/_runtime', null, /* Imports */[ ]); // From dart_utils - exportFrom(dart_utils, ['copyProperties', 'export']); + exportFrom(dart_utils, ['copyProperties', 'export_']); // Renames exports.defineLazyClass = _export(dart_utils.defineLazy); exports.defineLazyProperties = _export(dart_utils.defineLazy); diff --git a/pkg/dev_compiler/lib/runtime/dart/_types.js b/pkg/dev_compiler/lib/runtime/dart/_types.js index 225153813c66..76556262777a 100644 --- a/pkg/dev_compiler/lib/runtime/dart/_types.js +++ b/pkg/dev_compiler/lib/runtime/dart/_types.js @@ -7,15 +7,16 @@ dart_library.library('dart/_types', null, /* Imports */[ ], /* Lazy Imports */[ + 'dart/_utils', 'dart/core', 'dart/_classes', 'dart/_rtti' -], function(exports, core, classes, rtti) { +], function(exports, dart_utils, core, classes, rtti) { 'use strict'; const getOwnPropertyNames = Object.getOwnPropertyNames; - const assert = dart_utils.assert; + const assert = dart_utils.assert_; /** * Types in dart are represented at runtime as follows. @@ -24,12 +25,12 @@ dart_library.library('dart/_types', null, /* Imports */[ * If the type is the result of instantiating a generic class, * then the "classes" module manages the association between the * instantiated class and the original class declaration - * and the type arguments with which it was instantiated. This + * and the type arguments with which it was instantiated. This * assocation can be queried via the "classes" module". * * - All other types are represented as instances of class TypeRep, * defined in this module. - * - Dynamic, Void, and Bottom are singleton instances of sentinal + * - Dynamic, Void, and Bottom are singleton instances of sentinal * classes. * - Function types are instances of subclasses of AbstractFunctionType. * diff --git a/pkg/dev_compiler/lib/runtime/dart_utils.js b/pkg/dev_compiler/lib/runtime/dart/_utils.js similarity index 50% rename from pkg/dev_compiler/lib/runtime/dart_utils.js rename to pkg/dev_compiler/lib/runtime/dart/_utils.js index a7490b9df544..5dee57077110 100644 --- a/pkg/dev_compiler/lib/runtime/dart_utils.js +++ b/pkg/dev_compiler/lib/runtime/dart/_utils.js @@ -1,70 +1,42 @@ -// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -/* This library defines a set of general javascript utilities for us - * by the Dart runtime. -*/ - -var dart_utils = - typeof module != "undefined" && module.exports || {}; - -(function (dart_utils) { +dart_library.library('dart/_utils', null, /* Imports */[ +], /* Lazy imports */[ +], function(exports, dart) { 'use strict'; - const defineProperty = Object.defineProperty; const getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; const getOwnPropertyNames = Object.getOwnPropertyNames; const getOwnPropertySymbols = Object.getOwnPropertySymbols; - const hasOwnProperty = Object.prototype.hasOwnProperty; - - class StrongModeError extends Error { - constructor(message) { - super(message); + const StrongModeError = (function() { + function StrongModeError(message) { + Error.call(this); + this.message = message; } - } - - /** This error indicates a strong mode specific failure. - */ + ; + Object.setPrototypeOf(StrongModeError.prototype, Error.prototype); + return StrongModeError; + })(); function throwStrongModeError(message) { throw new StrongModeError(message); } - dart_utils.throwStrongModeError = throwStrongModeError; - - /** This error indicates a bug in the runtime or the compiler. - */ function throwInternalError(message) { throw Error(message); } - dart_utils.throwInternalError = throwInternalError; - - function assert(condition) { - if (!condition) throwInternalError("The compiler is broken: failed assert"); + function assert_(condition) { + if (!condition) + throwInternalError("The compiler is broken: failed assert"); } - dart_utils.assert = assert; - function getOwnNamesAndSymbols(obj) { return getOwnPropertyNames(obj).concat(getOwnPropertySymbols(obj)); } - dart_utils.getOwnNamesAndSymbols = getOwnNamesAndSymbols; - function safeGetOwnProperty(obj, name) { let desc = getOwnPropertyDescriptor(obj, name); - if (desc) return desc.value; + if (desc) + return desc.value; } - dart_utils.safeGetOwnProperty = safeGetOwnProperty; - - /** - * Defines a lazy property. - * After initial get or set, it will replace itself with a value property. - */ - // TODO(jmesserly): reusing descriptor objects has been shown to improve - // performance in other projects (e.g. webcomponents.js ShadowDOM polyfill). function defineLazyProperty(to, name, desc) { let init = desc.get; let value = null; - function lazySetter(x) { init = null; value = x; @@ -73,9 +45,8 @@ var dart_utils = throwInternalError('circular initialization for field ' + name); } function lazyGetter() { - if (init == null) return value; - - // Compute and store the value, guarding against reentry. + if (init == null) + return value; let f = init; init = circularInitError; lazySetter(f()); @@ -83,51 +54,53 @@ var dart_utils = } desc.get = lazyGetter; desc.configurable = true; - if (desc.set) desc.set = lazySetter; + if (desc.set) + desc.set = lazySetter; return defineProperty(to, name, desc); } - dart_utils.defineLazyProperty = defineLazyProperty; - function defineLazy(to, from) { for (let name of getOwnNamesAndSymbols(from)) { defineLazyProperty(to, name, getOwnPropertyDescriptor(from, name)); } } - dart_utils.defineLazy = defineLazy; - function defineMemoizedGetter(obj, name, getter) { return defineLazyProperty(obj, name, {get: getter}); } - dart_utils.defineMemoizedGetter = defineMemoizedGetter; - function copyTheseProperties(to, from, names) { for (let name of names) { defineProperty(to, name, getOwnPropertyDescriptor(from, name)); } return to; } - dart_utils.copyTheseProperties = copyTheseProperties; - - /** - * Copy properties from source to destination object. - * This operation is commonly called `mixin` in JS. - */ function copyProperties(to, from) { return copyTheseProperties(to, from, getOwnNamesAndSymbols(from)); } - dart_utils.copyProperties = copyProperties; - - /** Exports from one Dart module to another. */ function export_(to, from, show, hide) { if (show == void 0) { show = getOwnNamesAndSymbols(from); } if (hide != void 0) { var hideMap = new Set(hide); - show = show.filter((k) => !hideMap.has(k)); + show = show.filter(k => !hideMap.has(k)); } return copyTheseProperties(to, from, show); } - dart_utils.export = export_; - -})(dart_utils); + // Exports: + exports.defineProperty = defineProperty; + exports.getOwnPropertyDescriptor = getOwnPropertyDescriptor; + exports.getOwnPropertyNames = getOwnPropertyNames; + exports.getOwnPropertySymbols = getOwnPropertySymbols; + exports.hasOwnProperty = hasOwnProperty; + exports.StrongModeError = StrongModeError; + exports.throwStrongModeError = throwStrongModeError; + exports.throwInternalError = throwInternalError; + exports.assert_ = assert_; + exports.getOwnNamesAndSymbols = getOwnNamesAndSymbols; + exports.safeGetOwnProperty = safeGetOwnProperty; + exports.defineLazyProperty = defineLazyProperty; + exports.defineLazy = defineLazy; + exports.defineMemoizedGetter = defineMemoizedGetter; + exports.copyTheseProperties = copyTheseProperties; + exports.copyProperties = copyProperties; + exports.export_ = export_; +}); diff --git a/pkg/dev_compiler/lib/runtime/dart_library.js b/pkg/dev_compiler/lib/runtime/dart_library.js index 03d7bf612f94..12852bbf4dd5 100644 --- a/pkg/dev_compiler/lib/runtime/dart_library.js +++ b/pkg/dev_compiler/lib/runtime/dart_library.js @@ -11,6 +11,11 @@ var dart_library = (function (dart_library) { 'use strict'; + /** Note that we cannot use dart_utils.throwInternalError from here. */ + function throwLibraryError(message) { + throw Error(message); + } + // Module support. This is a simplified module system for Dart. // Longer term, we can easily migrate to an existing JS module system: // ES6, AMD, RequireJS, .... @@ -48,7 +53,7 @@ var dart_library = for (let name of list) { let lib = libraries[name]; if (!lib) { - dart_utils.throwInternalError('Library not available: ' + name); + throwLibraryError('Library not available: ' + name); } results.push(handler(lib)); } @@ -58,7 +63,7 @@ var dart_library = load(inheritedPendingSet) { // Check for cycles if (this._state == LibraryLoader.LOADING) { - dart_utils.throwInternalError('Circular dependence on library: ' + throwLibraryError('Circular dependence on library: ' + this._name); } else if (this._state >= LibraryLoader.LOADED) { return this._library; @@ -107,7 +112,7 @@ var dart_library = let loader = libraries[libraryName]; // TODO(vsm): A user might call this directly from JS (as we do in tests). // We may want a different error type. - if (!loader) dart_utils.throwInternalError('Library not found: ' + libraryName); + if (!loader) throwLibraryError('Library not found: ' + libraryName); return loader.load(); } dart_library.import = import_; diff --git a/pkg/dev_compiler/lib/src/codegen/js_codegen.dart b/pkg/dev_compiler/lib/src/codegen/js_codegen.dart index 5e1b8a6ca228..9879bd42cd72 100644 --- a/pkg/dev_compiler/lib/src/codegen/js_codegen.dart +++ b/pkg/dev_compiler/lib/src/codegen/js_codegen.dart @@ -37,6 +37,7 @@ import 'js_metalet.dart' as JS; import 'js_module_item_order.dart'; import 'js_printer.dart' show writeJsLibrary; import 'side_effect_analysis.dart'; +import 'package:collection/equality.dart'; // Various dynamic helpers we call. // If renaming these, make sure to check other places like the @@ -50,6 +51,8 @@ const DSETINDEX = 'dsetindex'; const DCALL = 'dcall'; const DSEND = 'dsend'; +const ListEquality _listEquality = const ListEquality(); + class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator { final AbstractCompiler compiler; final CodegenOptions options; @@ -104,6 +107,8 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator { /// The default value of the module object. See [visitLibraryDirective]. String _jsModuleValue; + bool _isDartUtils; + Map _objectMembers; JSCodegenVisitor(AbstractCompiler compiler, this.rules, this.currentLibrary, @@ -117,6 +122,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator { var src = context.sourceFactory.forUri('dart:_interceptors'); var interceptors = context.computeLibraryElement(src); _jsArray = interceptors.getType('JSArray'); + _isDartUtils = currentLibrary.source.uri.toString() == 'dart:_utils'; _objectMembers = getObjectMemberMap(types); } @@ -196,7 +202,19 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator { list.add(js.string(compiler.getModuleName(library.source.uri), "'")); }; - var imports = [js.string('dart/_runtime')]; + var needsDartRuntime = !_isDartUtils; + + var imports = []; + var moduleStatements = []; + if (needsDartRuntime) { + imports.add(js.string('dart/_runtime')); + + var dartxImport = + js.statement("let # = #.dartx;", [_dartxVar, _runtimeLibVar]); + moduleStatements.add(dartxImport); + } + moduleStatements.addAll(_moduleItems); + _imports.forEach((library, temp) { if (_loader.libraryIsLoaded(library)) { processImport(library, temp, imports); @@ -210,11 +228,8 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator { } }); - var dartxImport = - js.statement("let # = #.dartx;", [_dartxVar, _runtimeLibVar]); - - var module = js.call("function(#) { 'use strict'; #; #; }", - [params, dartxImport, _moduleItems]); + var module = + js.call("function(#) { 'use strict'; #; }", [params, moduleStatements]); var moduleDef = js.statement("dart_library.library(#, #, #, #, #)", [ js.string(jsPath, "'"), @@ -289,7 +304,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator { args.add(new JS.ArrayInitializer(shownNames)); args.add(new JS.ArrayInitializer(hiddenNames)); } - _moduleItems.add(js.statement('dart.export(#);', [args])); + _moduleItems.add(js.statement('dart.export_(#);', [args])); } JS.Identifier _initSymbol(JS.Identifier id) { @@ -1285,17 +1300,69 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator { var name = node.name.name; + var fn = _visit(node.functionExpression); + bool needsTagging = true; + + if (currentLibrary.source.isInSystemLibrary && + _isInlineJSFunction(node.functionExpression)) { + fn = _simplifyPassThroughArrowFunCallBody(fn); + needsTagging = !_isDartUtils; + } + var id = new JS.Identifier(name); - body.add(annotate( - new JS.FunctionDeclaration(id, _visit(node.functionExpression)), - node.element)); - body.add(_emitFunctionTagged(id, node.element.type, topLevel: true) - .toStatement()); + body.add(annotate(new JS.FunctionDeclaration(id, fn), node.element)); + if (needsTagging) { + body.add(_emitFunctionTagged(id, node.element.type, topLevel: true) + .toStatement()); + } if (isPublic(name)) _addExport(name); return _statement(body); } + bool _isInlineJSFunction(FunctionExpression functionExpression) { + bool isJsInvocation(Expression expr) => + expr is MethodInvocation && isInlineJS(expr.methodName.staticElement); + + var body = functionExpression.body; + if (body is ExpressionFunctionBody) { + return isJsInvocation(body.expression); + } else if (body is BlockFunctionBody) { + if (body.block.statements.length == 1) { + var stat = body.block.statements.single; + if (stat is ReturnStatement) { + return isJsInvocation(stat.expression); + } + } + } + return false; + } + + // Simplify `(args) => ((x, y) => { ... })(x, y)` to `(args) => { ... }`. + // Note: we don't check if the top-level args match the ones passed through + // the arrow function, which allows silently passing args through to the + // body (which only works if we don't do weird renamings of Dart params). + JS.Fun _simplifyPassThroughArrowFunCallBody(JS.Fun fn) { + String getIdent(JS.Node node) => node is JS.Identifier ? node.name : null; + List getIdents(List params) => + params.map(getIdent).toList(growable: false); + + if (fn.body is JS.Block && fn.body.statements.length == 1) { + var stat = fn.body.statements.single; + if (stat is JS.Return && stat.value is JS.Call) { + JS.Call call = stat.value; + if (call.target is JS.ArrowFun) { + var passedArgs = getIdents(call.arguments); + JS.ArrowFun innerFun = call.target; + if (_listEquality.equals(getIdents(innerFun.params), passedArgs)) { + return new JS.Fun(fn.params, innerFun.body); + } + } + } + } + return fn; + } + JS.Method _emitTopLevelProperty(FunctionDeclaration node) { var name = node.name.name; return annotate( diff --git a/pkg/dev_compiler/lib/src/compiler.dart b/pkg/dev_compiler/lib/src/compiler.dart index 65e75a3dcc24..1b764eeb49b8 100644 --- a/pkg/dev_compiler/lib/src/compiler.dart +++ b/pkg/dev_compiler/lib/src/compiler.dart @@ -520,8 +520,8 @@ const corelibOrder = const [ final defaultRuntimeFiles = () { var files = [ 'harmony_feature_check.js', - 'dart_utils.js', 'dart_library.js', + 'dart/_utils.js', 'dart/_errors.js', 'dart/_generators.js', 'dart/_types.js', diff --git a/pkg/dev_compiler/lib/src/runner/runtime_utils.dart b/pkg/dev_compiler/lib/src/runner/runtime_utils.dart index 647c62e41f40..25f6fd20f2a5 100644 --- a/pkg/dev_compiler/lib/src/runner/runtime_utils.dart +++ b/pkg/dev_compiler/lib/src/runner/runtime_utils.dart @@ -19,7 +19,7 @@ import 'file_utils.dart'; /// TODO(ochafik): Investigate alternative module / alias patterns. const _ALIASED_RUNTIME_FILES = const { 'dart_library.js': 'dart_library', - 'dart_utils.js': 'dart_utils', + 'dart/_utils.js': 'dart_utils', }; /// If [path] is a runtime file with an alias, returns that alias, otherwise diff --git a/pkg/dev_compiler/pubspec.yaml b/pkg/dev_compiler/pubspec.yaml index 6898e63b7438..24da769b114c 100644 --- a/pkg/dev_compiler/pubspec.yaml +++ b/pkg/dev_compiler/pubspec.yaml @@ -14,7 +14,7 @@ dependencies: cli_util: ^0.0.1 crypto: ^0.9.0 html: ^0.12.0 - js: ^0.6.0-beta.7 + js: ">=0.6.0 <0.7.0" logging: ">=0.9.2 <0.12.0" path: ^1.3.0 pub_semver: ^1.1.0 diff --git a/pkg/dev_compiler/test/browser/runtime_tests.js b/pkg/dev_compiler/test/browser/runtime_tests.js index a150373a0286..030be22ac847 100644 --- a/pkg/dev_compiler/test/browser/runtime_tests.js +++ b/pkg/dev_compiler/test/browser/runtime_tests.js @@ -13,6 +13,7 @@ var dartx = dart.dartx; // If we decide to expose them, this can go away. var classes = dart_library.import('dart/_classes'); var types = dart_library.import('dart/_types'); +var dart_utils = dart_library.import('dart/_utils'); suite('generic', () => { "use strict"; diff --git a/pkg/dev_compiler/test/codegen/expect/collection/src/unmodifiable_wrappers.js b/pkg/dev_compiler/test/codegen/expect/collection/src/unmodifiable_wrappers.js index 8f3a05a62678..f7772210f597 100644 --- a/pkg/dev_compiler/test/codegen/expect/collection/src/unmodifiable_wrappers.js +++ b/pkg/dev_compiler/test/codegen/expect/collection/src/unmodifiable_wrappers.js @@ -7,7 +7,7 @@ dart_library.library('collection/src/unmodifiable_wrappers', null, /* Imports */ ], function(exports, dart, collection, core, wrappers) { 'use strict'; let dartx = dart.dartx; - dart.export(exports, collection, ['UnmodifiableListView', 'UnmodifiableMapView'], []); + dart.export_(exports, collection, ['UnmodifiableListView', 'UnmodifiableMapView'], []); const NonGrowableListMixin$ = dart.generic(function(E) { class NonGrowableListMixin extends core.Object { static _throw() { diff --git a/pkg/dev_compiler/test/codegen/expect/collection/wrappers.js b/pkg/dev_compiler/test/codegen/expect/collection/wrappers.js index 2440224be2cd..70b2afee44a1 100644 --- a/pkg/dev_compiler/test/codegen/expect/collection/wrappers.js +++ b/pkg/dev_compiler/test/codegen/expect/collection/wrappers.js @@ -9,8 +9,8 @@ dart_library.library('collection/wrappers', null, /* Imports */[ ], function(exports, dart, canonicalized_map, core, math, collection, unmodifiable_wrappers) { 'use strict'; let dartx = dart.dartx; - dart.export(exports, canonicalized_map); - dart.export(exports, unmodifiable_wrappers); + dart.export_(exports, canonicalized_map); + dart.export_(exports, unmodifiable_wrappers); const _base = Symbol('_base'); const _DelegatingIterableBase$ = dart.generic(function(E) { class _DelegatingIterableBase extends core.Object { diff --git a/pkg/dev_compiler/test/codegen/expect/dir/html_input_b.js b/pkg/dev_compiler/test/codegen/expect/dir/html_input_b.js index 0e52aea0fb0d..a298fcb85820 100644 --- a/pkg/dev_compiler/test/codegen/expect/dir/html_input_b.js +++ b/pkg/dev_compiler/test/codegen/expect/dir/html_input_b.js @@ -5,6 +5,6 @@ dart_library.library('dir/html_input_b', null, /* Imports */[ ], function(exports, dart, html_input_d) { 'use strict'; let dartx = dart.dartx; - dart.export(exports, html_input_d); + dart.export_(exports, html_input_d); exports.x = 3; }); diff --git a/pkg/dev_compiler/test/codegen/expect/html_input.html b/pkg/dev_compiler/test/codegen/expect/html_input.html index 060cda11372e..e661f0116717 100644 --- a/pkg/dev_compiler/test/codegen/expect/html_input.html +++ b/pkg/dev_compiler/test/codegen/expect/html_input.html @@ -1,7 +1,7 @@ - + diff --git a/pkg/dev_compiler/test/codegen/expect/js/js.js b/pkg/dev_compiler/test/codegen/expect/js/js.js index c5ca15291a06..bba30b0893a3 100644 --- a/pkg/dev_compiler/test/codegen/expect/js/js.js +++ b/pkg/dev_compiler/test/codegen/expect/js/js.js @@ -6,7 +6,7 @@ dart_library.library('js/js', null, /* Imports */[ ], function(exports, dart, js, core) { 'use strict'; let dartx = dart.dartx; - dart.export(exports, js, ['allowInterop', 'allowInteropCaptureThis'], []); + dart.export_(exports, js, ['allowInterop', 'allowInteropCaptureThis'], []); class JS extends core.Object { JS(name) { if (name === void 0) diff --git a/pkg/dev_compiler/test/codegen/expect/sunflower/sunflower.html b/pkg/dev_compiler/test/codegen/expect/sunflower/sunflower.html index 0e8d2bfe35ec..9719fce5f4d3 100644 --- a/pkg/dev_compiler/test/codegen/expect/sunflower/sunflower.html +++ b/pkg/dev_compiler/test/codegen/expect/sunflower/sunflower.html @@ -24,8 +24,8 @@

drfibonacci's Sunflower Spectacular

- + diff --git a/pkg/dev_compiler/test/codegen/expect/unittest.js b/pkg/dev_compiler/test/codegen/expect/unittest.js index 8a6fff1c9bc4..2018b5bad47b 100644 --- a/pkg/dev_compiler/test/codegen/expect/unittest.js +++ b/pkg/dev_compiler/test/codegen/expect/unittest.js @@ -11,7 +11,7 @@ dart_library.library('unittest', null, /* Imports */[ ], function(exports, dart, matcher, dom, core, async, interfaces, util, description$) { 'use strict'; let dartx = dart.dartx; - dart.export(exports, matcher); + dart.export_(exports, matcher); function group(name, body) { return dart.dsend(dom.window, 'suite', name, body); } diff --git a/pkg/dev_compiler/test/codegen/expect/unittest/unittest.js b/pkg/dev_compiler/test/codegen/expect/unittest/unittest.js index dfb107ef93de..3e7a0f3a5af1 100644 --- a/pkg/dev_compiler/test/codegen/expect/unittest/unittest.js +++ b/pkg/dev_compiler/test/codegen/expect/unittest/unittest.js @@ -11,7 +11,7 @@ dart_library.library('unittest/unittest', null, /* Imports */[ ], function(exports, dart, matcher, dom, core, async, interfaces, util, description$) { 'use strict'; let dartx = dart.dartx; - dart.export(exports, matcher); + dart.export_(exports, matcher); function group(name, body) { return dart.dsend(dom.window, 'suite', name, body); } diff --git a/pkg/dev_compiler/tool/input_sdk/lib/_internal/libraries.dart b/pkg/dev_compiler/tool/input_sdk/lib/_internal/libraries.dart index 7594c938f9ef..3c3fd2f90491 100644 --- a/pkg/dev_compiler/tool/input_sdk/lib/_internal/libraries.dart +++ b/pkg/dev_compiler/tool/input_sdk/lib/_internal/libraries.dart @@ -216,6 +216,12 @@ const Map LIBRARIES = const { category: "Internal", documented: false, platforms: DART2JS_PLATFORM), + + "_utils": const LibraryInfo( + "_internal/compiler/js_lib/utils.dart", + category: "Internal", + documented: false, + platforms: DART2JS_PLATFORM), }; /** diff --git a/pkg/dev_compiler/tool/input_sdk/private/js_primitives.dart b/pkg/dev_compiler/tool/input_sdk/private/js_primitives.dart index 389471dc3ec0..9c3a4d4842b9 100644 --- a/pkg/dev_compiler/tool/input_sdk/private/js_primitives.dart +++ b/pkg/dev_compiler/tool/input_sdk/private/js_primitives.dart @@ -9,6 +9,11 @@ library dart2js._js_primitives; import 'dart:_foreign_helper' show JS; +// This import is here temporarily to cause utils.dart to be compiled. +// TODO(ochafik): Remove this phony import once other Dart files actually depend +// on utils. +import 'dart:_utils'; + /** * This is the low-level method that is used to implement [print]. It is * possible to override this function from JavaScript by defining a function in diff --git a/pkg/dev_compiler/tool/input_sdk/private/utils.dart b/pkg/dev_compiler/tool/input_sdk/private/utils.dart new file mode 100644 index 000000000000..c4a81ea14de2 --- /dev/null +++ b/pkg/dev_compiler/tool/input_sdk/private/utils.dart @@ -0,0 +1,123 @@ +// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +library dart._utils; + +import 'dart:_foreign_helper' show JS; + +/// This library defines a set of general javascript utilities for us +/// by the Dart runtime. +// TODO(ochafik): Rewrite some of these in Dart when possible. + +// TODO(ochafik): Make these final + special-case them in js_codegen to fix +// Analyzer errors. +const defineProperty = JS('', 'Object.defineProperty'); +const getOwnPropertyDescriptor = JS('', 'Object.getOwnPropertyDescriptor'); +const getOwnPropertyNames = JS('', 'Object.getOwnPropertyNames'); +const getOwnPropertySymbols = JS('', 'Object.getOwnPropertySymbols'); + +const hasOwnProperty = JS('', 'Object.prototype.hasOwnProperty'); + +// TODO(ochafik): Add ES6 class syntax support to JS intrinsics to avoid this. +const StrongModeError = JS('', '''(function() { + function StrongModeError(message) { + Error.call(this); + this.message = message; + }; + Object.setPrototypeOf(StrongModeError.prototype, Error.prototype); + return StrongModeError; +})()'''); + +/// This error indicates a strong mode specific failure. +void throwStrongModeError(String message) => JS('', '''((message) => { + throw new StrongModeError(message); +})(#)''', message); + +/// This error indicates a bug in the runtime or the compiler. +void throwInternalError(String message) => JS('', '''((message) => { + throw Error(message); +})(#)''', message); + +// TODO(ochafik): Re-introduce a @JS annotation in the SDK (same as package:js) +// so that this is named 'assert' in JavaScript. +void assert_(bool condition) => JS('', '''((condition) => { + if (!condition) throwInternalError("The compiler is broken: failed assert"); +})(#)''', condition); + +getOwnNamesAndSymbols(obj) => JS('', '''((obj) => { + return getOwnPropertyNames(obj).concat(getOwnPropertySymbols(obj)); +})(#)''', obj); + +safeGetOwnProperty(obj, String name) => JS('', '''((obj, name) => { + let desc = getOwnPropertyDescriptor(obj, name); + if (desc) return desc.value; +})(#, #)''', obj, name); + +/// Defines a lazy property. +/// After initial get or set, it will replace itself with a value property. +// TODO(jmesserly): reusing descriptor objects has been shown to improve +// performance in other projects (e.g. webcomponents.js ShadowDOM polyfill). +defineLazyProperty(to, name, desc) => JS('', '''((to, name, desc) => { + let init = desc.get; + let value = null; + + function lazySetter(x) { + init = null; + value = x; + } + function circularInitError() { + throwInternalError('circular initialization for field ' + name); + } + function lazyGetter() { + if (init == null) return value; + + // Compute and store the value, guarding against reentry. + let f = init; + init = circularInitError; + lazySetter(f()); + return value; + } + desc.get = lazyGetter; + desc.configurable = true; + if (desc.set) desc.set = lazySetter; + return defineProperty(to, name, desc); +})(#, #, #)''', to, name, desc); + +void defineLazy(to, from) => JS('', '''((to, from) => { + for (let name of getOwnNamesAndSymbols(from)) { + defineLazyProperty(to, name, getOwnPropertyDescriptor(from, name)); + } +})(#, #)''', to, from); + +defineMemoizedGetter(obj, String name, getter) => + JS('', '''((obj, name, getter) => { + return defineLazyProperty(obj, name, {get: getter}); +})(#, #, #)''', obj, name, getter); + +copyTheseProperties(to, from, names) => JS('', '''((to, from, names) => { + for (let name of names) { + defineProperty(to, name, getOwnPropertyDescriptor(from, name)); + } + return to; +})(#, #, #)''', to, from, names); + +/// Copy properties from source to destination object. +/// This operation is commonly called `mixin` in JS. +copyProperties(to, from) => JS('', '''((to, from) => { + return copyTheseProperties(to, from, getOwnNamesAndSymbols(from)); +})(#, #)''', to, from); + +/// Exports from one Dart module to another. +// TODO(ochafik): Re-introduce a @JS annotation in the SDK (same as package:js) +// so that this is named 'export' in JavaScript. +export_(to, from, show, hide) => JS('', '''((to, from, show, hide) => { + if (show == void 0) { + show = getOwnNamesAndSymbols(from); + } + if (hide != void 0) { + var hideMap = new Set(hide); + show = show.filter((k) => !hideMap.has(k)); + } + return copyTheseProperties(to, from, show); +})(#, #, #, #)''', to, from, show, hide); diff --git a/pkg/dev_compiler/tool/sdk_expected_errors.txt b/pkg/dev_compiler/tool/sdk_expected_errors.txt index 42237337e152..994b444070e4 100644 --- a/pkg/dev_compiler/tool/sdk_expected_errors.txt +++ b/pkg/dev_compiler/tool/sdk_expected_errors.txt @@ -1,6 +1,12 @@ severe: [AnalyzerMessage] The redirected constructor '(dynamic) → JSArray' has incompatible parameters with '(dynamic) → JSArray' (dart:_interceptors/js_array.dart, line 29, col 46) severe: [AnalyzerMessage] Missing concrete implementation of 'num.==' (dart:_interceptors/js_number.dart, line 12, col 7) severe: [AnalyzerMessage] Missing concrete implementation of 'String.==' (dart:_interceptors/js_string.dart, line 14, col 7) +severe: [AnalyzerMessage] 'const' variables must be constant value (dart:_utils, line 15, col 24) +severe: [AnalyzerMessage] 'const' variables must be constant value (dart:_utils, line 16, col 34) +severe: [AnalyzerMessage] 'const' variables must be constant value (dart:_utils, line 17, col 29) +severe: [AnalyzerMessage] 'const' variables must be constant value (dart:_utils, line 18, col 31) +severe: [AnalyzerMessage] 'const' variables must be constant value (dart:_utils, line 20, col 24) +severe: [AnalyzerMessage] 'const' variables must be constant value (dart:_utils, line 23, col 25) severe: [AnalyzerMessage] The redirected constructor '((Stream, bool) → StreamSubscription) → _StreamSubscriptionTransformer' has incompatible parameters with '((Stream, bool) → StreamSubscription) → StreamTransformer' (dart:async/stream.dart, line 1571, col 9) severe: [AnalyzerMessage] The redirected constructor '({handleData: (dynamic, EventSink) → void, handleError: (Object, StackTrace, EventSink) → void, handleDone: (EventSink) → void}) → _StreamHandlerTransformer' has incompatible parameters with '({handleData: (S, EventSink) → void, handleError: (Object, StackTrace, EventSink) → void, handleDone: (EventSink) → void}) → StreamTransformer' (dart:async/stream.dart, line 1588, col 13) severe: [AnalyzerMessage] The type '_NoCallbackSyncStreamController' is declared with 0 type parameters, but 1 type arguments were given (dart:async/stream_controller.dart, line 84, col 17)