Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 import 'dart:async'; | 5 import 'dart:async'; |
| 6 import 'dart:io'; | 6 import 'dart:io'; |
| 7 import 'dart:convert' show JsonEncoder; | |
| 7 | 8 |
| 8 import 'package:analyzer/analyzer.dart'; | 9 import 'package:analyzer/analyzer.dart'; |
| 9 import 'package:barback/barback.dart'; | 10 import 'package:barback/barback.dart'; |
| 10 import 'package:bazel_worker/bazel_worker.dart'; | 11 import 'package:bazel_worker/bazel_worker.dart'; |
| 11 import 'package:cli_util/cli_util.dart' as cli_util; | 12 import 'package:cli_util/cli_util.dart' as cli_util; |
| 12 import 'package:path/path.dart' as p; | 13 import 'package:path/path.dart' as p; |
| 13 | 14 |
| 14 import '../dart.dart'; | 15 import '../dart.dart'; |
| 15 import '../io.dart'; | 16 import '../io.dart'; |
| 17 import 'module.dart'; | |
| 16 import 'module_reader.dart'; | 18 import 'module_reader.dart'; |
| 17 import 'scratch_space.dart'; | 19 import 'scratch_space.dart'; |
| 18 import 'summaries.dart'; | 20 import 'summaries.dart'; |
| 19 import 'workers.dart'; | 21 import 'workers.dart'; |
| 20 | 22 |
| 23 // JavaScript snippet to determine the directory a script was run from. | |
|
Bob Nystrom
2017/05/22 20:21:43
Nit: "///".
| |
| 24 final _currentDirectoryScript = r''' | |
| 25 var _currentDirectory = (function () { | |
| 26 var _url; | |
| 27 var lines = new Error().stack.split('\n'); | |
| 28 function lookupUrl() { | |
| 29 if (lines.length > 2) { | |
| 30 var match = lines[1].match(/^\s+at (.+):\d+:\d+$/); | |
| 31 // Chrome. | |
| 32 if (match) return match[1]; | |
| 33 // Chrome nested eval case. | |
| 34 match = lines[1].match(/^\s+at eval [(](.+):\d+:\d+[)]$/); | |
| 35 if (match) return match[1]; | |
| 36 // Edge. | |
| 37 match = lines[1].match(/^\s+at.+\((.+):\d+:\d+\)$/); | |
| 38 if (match) return match[1]; | |
| 39 // Firefox. | |
| 40 return lines[0].match(/[<][@](.+):\d+:\d+$/)[1]; | |
| 41 } | |
| 42 // Safari. | |
| 43 return lines[0].match(/(.+):\d+:\d+$/)[1]; | |
| 44 } | |
| 45 _url = lookupUrl(); | |
| 46 var lastSlash = _url.lastIndexOf('/'); | |
| 47 if (lastSlash == -1) return _url; | |
| 48 var currentDirectory = _url.substring(0, lastSlash + 1); | |
| 49 return currentDirectory; | |
| 50 })(); | |
| 51 '''; | |
| 52 | |
| 21 /// Returns whether or not [dartId] is an app entrypoint (basically, whether or | 53 /// Returns whether or not [dartId] is an app entrypoint (basically, whether or |
| 22 /// not it has a `main` function). | 54 /// not it has a `main` function). |
| 23 Future<bool> isAppEntryPoint( | 55 Future<bool> isAppEntryPoint( |
| 24 AssetId dartId, Future<Asset> getAsset(AssetId id)) async { | 56 AssetId dartId, Future<Asset> getAsset(AssetId id)) async { |
| 25 assert(dartId.extension == '.dart'); | 57 assert(dartId.extension == '.dart'); |
| 26 var dartAsset = await getAsset(dartId); | 58 var dartAsset = await getAsset(dartId); |
| 27 // Skip reporting errors here, dartdevc will report them later with nicer | 59 // Skip reporting errors here, dartdevc will report them later with nicer |
| 28 // formatting. | 60 // formatting. |
| 29 var parsed = parseCompilationUnit(await dartAsset.readAsString(), | 61 var parsed = parseCompilationUnit(await dartAsset.readAsString(), |
| 30 suppressErrors: true); | 62 suppressErrors: true); |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 53 ModuleReader moduleReader, | 85 ModuleReader moduleReader, |
| 54 Future<Asset> getAsset(AssetId id)) { | 86 Future<Asset> getAsset(AssetId id)) { |
| 55 var bootstrapId = dartEntrypointId.addExtension('.bootstrap.js'); | 87 var bootstrapId = dartEntrypointId.addExtension('.bootstrap.js'); |
| 56 var jsEntrypointId = dartEntrypointId.addExtension('.js'); | 88 var jsEntrypointId = dartEntrypointId.addExtension('.js'); |
| 57 var jsMapEntrypointId = jsEntrypointId.addExtension('.map'); | 89 var jsMapEntrypointId = jsEntrypointId.addExtension('.map'); |
| 58 | 90 |
| 59 var outputCompleters = <AssetId, Completer<Asset>>{ | 91 var outputCompleters = <AssetId, Completer<Asset>>{ |
| 60 bootstrapId: new Completer(), | 92 bootstrapId: new Completer(), |
| 61 jsEntrypointId: new Completer(), | 93 jsEntrypointId: new Completer(), |
| 62 }; | 94 }; |
| 63 if (mode == BarbackMode.DEBUG) { | 95 var debugMode = mode == BarbackMode.DEBUG; |
|
Bob Nystrom
2017/05/22 20:21:43
Nit: I think "isDebug" would make it a little clea
| |
| 96 | |
| 97 if (debugMode) { | |
| 64 outputCompleters[jsMapEntrypointId] = new Completer<Asset>(); | 98 outputCompleters[jsMapEntrypointId] = new Completer<Asset>(); |
| 65 } | 99 } |
| 66 | 100 |
| 67 return _ensureComplete(outputCompleters, () async { | 101 return _ensureComplete(outputCompleters, () async { |
| 68 var module = await moduleReader.moduleFor(dartEntrypointId); | 102 var module = await moduleReader.moduleFor(dartEntrypointId); |
| 69 if (module == null) return; | 103 if (module == null) return; |
| 70 | 104 |
| 71 // The path to the entrypoint JS module as it should appear in the call to | 105 // The path to the entrypoint JS module as it should appear in the call to |
| 72 // `require` in the bootstrap file. | 106 // `require` in the bootstrap file. |
| 73 var moduleDir = topLevelDir(dartEntrypointId.path); | 107 var moduleDir = topLevelDir(dartEntrypointId.path); |
| 74 var appModulePath = p.url.relative(p.url.join(moduleDir, module.id.name), | 108 var appModulePath = p.url.relative(p.url.join(moduleDir, module.id.name), |
| 75 from: p.url.dirname(dartEntrypointId.path)); | 109 from: p.url.dirname(dartEntrypointId.path)); |
| 76 | 110 |
| 77 // The name of the entrypoint dart library within the entrypoint JS module. | 111 // The name of the entrypoint dart library within the entrypoint JS module. |
| 78 // | 112 // |
| 79 // This is used to invoke `main()` from within the bootstrap script. | 113 // This is used to invoke `main()` from within the bootstrap script. |
| 80 // | 114 // |
| 81 // TODO(jakemac53): Sane module name creation, this only works in the most | 115 // TODO(jakemac53): Sane module name creation, this only works in the most |
| 82 // basic of cases. | 116 // basic of cases. |
| 83 // | 117 // |
| 84 // See https://github.com/dart-lang/sdk/issues/27262 for the root issue | 118 // See https://github.com/dart-lang/sdk/issues/27262 for the root issue |
| 85 // which will allow us to not rely on the naming schemes that dartdevc uses | 119 // which will allow us to not rely on the naming schemes that dartdevc uses |
| 86 // internally, but instead specify our own. | 120 // internally, but instead specify our own. |
| 87 var appModuleScope = p.url | 121 var appModuleScope = p.url |
| 88 .split(p.url.withoutExtension( | 122 .split(p.url.withoutExtension( |
| 89 p.url.relative(dartEntrypointId.path, from: moduleDir))) | 123 p.url.relative(dartEntrypointId.path, from: moduleDir))) |
| 90 .join("__") | 124 .join("__") |
| 91 .replaceAll('.', '\$46'); | 125 .replaceAll('.', '\$46'); |
| 92 | 126 |
| 93 // Modules not under a `packages` directory need custom module paths. | 127 // Map from module name to module path. |
| 94 var customModulePaths = <String>[]; | 128 // Modules outside of the `packages` directory have different module path |
| 129 // and module names. | |
| 130 var modulePaths = new Map<String, String>(); | |
|
Bob Nystrom
2017/05/22 20:21:44
We have map literals.
var modulePaths = {
appMo
| |
| 131 modulePaths[appModulePath] = appModulePath; | |
| 132 modulePaths['dart_sdk'] = 'dart_sdk'; | |
| 133 | |
| 95 var transitiveDeps = await moduleReader.readTransitiveDeps(module); | 134 var transitiveDeps = await moduleReader.readTransitiveDeps(module); |
| 96 for (var dep in transitiveDeps) { | 135 for (var dep in transitiveDeps) { |
| 97 if (dep.dir != 'lib') { | 136 if (dep.dir != 'lib') { |
| 98 var relativePath = p.url.relative(p.url.join(moduleDir, dep.name), | 137 var relativePath = p.url.relative(p.url.join(moduleDir, dep.name), |
| 99 from: p.url.dirname(bootstrapId.path)); | 138 from: p.url.dirname(bootstrapId.path)); |
| 100 customModulePaths.add('"${dep.dir}/${dep.name}": "$relativePath"'); | 139 var jsModuleName = '${dep.dir}/${dep.name}'; |
| 140 | |
| 141 modulePaths[jsModuleName] = relativePath; | |
| 101 } else { | 142 } else { |
| 102 var jsModuleName = 'packages/${dep.package}/${dep.name}'; | 143 var jsModuleName = 'packages/${dep.package}/${dep.name}'; |
| 103 var actualModulePath = p.url.relative( | 144 var actualModulePath = p.url.relative( |
| 104 p.url.join(moduleDir, jsModuleName), | 145 p.url.join(moduleDir, jsModuleName), |
| 105 from: p.url.dirname(bootstrapId.path)); | 146 from: p.url.dirname(bootstrapId.path)); |
| 106 if (jsModuleName != actualModulePath) { | 147 modulePaths[jsModuleName] = actualModulePath; |
| 107 customModulePaths.add('"$jsModuleName": "$actualModulePath"'); | |
| 108 } | |
| 109 } | 148 } |
| 110 } | 149 } |
| 150 var bootstrapContent = new StringBuffer('(function() {\n'); | |
| 151 if (debugMode) { | |
| 152 bootstrapContent.write(''' | |
| 153 $_currentDirectoryScript | |
| 154 let modulePaths = ${const JsonEncoder.withIndent(" ").convert(modulePaths)}; | |
| 111 | 155 |
| 112 var bootstrapContent = ''' | 156 if(!window.\$dartLoader) { |
| 157 window.\$dartLoader = { | |
| 158 moduleIdToUrl: new Map(), | |
| 159 urlToModuleId: new Map(), | |
| 160 rootDirectories: new Set(), | |
| 161 }; | |
| 162 } | |
| 163 | |
| 164 let customModulePaths = {}; | |
| 165 window.\$dartLoader.rootDirectories.add(_currentDirectory); | |
| 166 for (let moduleName of Object.getOwnPropertyNames(modulePaths)) { | |
| 167 let modulePath = modulePaths[moduleName]; | |
| 168 if (modulePath != moduleName) { | |
| 169 customModulePaths[moduleName] = modulePath; | |
| 170 } | |
| 171 var src = _currentDirectory + modulePath + '.js'; | |
| 172 if (window.\$dartLoader.moduleIdToUrl.has(moduleName)) { | |
| 173 continue; | |
| 174 } | |
| 175 \$dartLoader.moduleIdToUrl.set(moduleName, src); | |
| 176 \$dartLoader.urlToModuleId.set(src, moduleName); | |
| 177 } | |
| 178 '''); | |
| 179 } else { | |
| 180 var customModulePaths = new Map<String, String>(); | |
|
Bob Nystrom
2017/05/22 20:21:44
<String, String>{}
| |
| 181 modulePaths.forEach((name, path) { | |
| 182 if (name != path) customModulePaths[name] = path; | |
| 183 }); | |
| 184 var json = const JsonEncoder.withIndent(" ").convert(customModulePaths); | |
| 185 bootstrapContent.write('let customModulePaths = ${json};\n'); | |
| 186 } | |
| 187 | |
| 188 bootstrapContent.write(''' | |
| 113 require.config({ | 189 require.config({ |
| 114 waitSeconds: 30, | 190 waitSeconds: 30, |
| 115 paths: { | 191 paths: customModulePaths |
| 116 ${customModulePaths.join(',\n ')} | |
| 117 } | |
| 118 }); | 192 }); |
| 119 | 193 |
| 120 require(["$appModulePath", "dart_sdk"], function(app, dart_sdk) { | 194 require(["$appModulePath", "dart_sdk"], function(app, dart_sdk) { |
| 121 dart_sdk._isolate_helper.startRootIsolate(() => {}, []); | 195 dart_sdk._isolate_helper.startRootIsolate(() => {}, []); |
| 196 '''); | |
| 197 | |
| 198 if (debugMode) { | |
| 199 bootstrapContent.write(''' | |
| 200 dart_sdk._debugger.registerDevtoolsFormatter(); | |
| 201 | |
| 202 if (window.\$dartStackTraceUtility && !window.\$dartStackTraceUtility.ready) { | |
| 203 window.\$dartStackTraceUtility.ready = true; | |
| 204 let dart = dart_sdk.dart; | |
| 205 window.\$dartStackTraceUtility.setSourceMapProvider( | |
| 206 function(url) { | |
| 207 var module = window.\$dartLoader.urlToModuleId.get(url); | |
| 208 if (!module) return null; | |
| 209 return dart.getSourceMap(module); | |
| 210 }); | |
| 211 } | |
| 212 window.postMessage({ type: "DDC_STATE_CHANGE", state: "start" }, "*"); | |
| 213 '''); | |
| 214 } | |
| 215 | |
| 216 bootstrapContent.write(''' | |
| 122 app.$appModuleScope.main(); | 217 app.$appModuleScope.main(); |
| 123 }); | 218 }); |
| 124 '''; | 219 })(); |
| 125 outputCompleters[bootstrapId] | 220 '''); |
| 126 .complete(new Asset.fromString(bootstrapId, bootstrapContent)); | 221 outputCompleters[bootstrapId].complete( |
| 222 new Asset.fromString(bootstrapId, bootstrapContent.toString())); | |
| 127 | 223 |
| 128 var bootstrapModuleName = p.withoutExtension( | 224 var bootstrapModuleName = p.withoutExtension( |
| 129 p.relative(bootstrapId.path, from: p.dirname(dartEntrypointId.path))); | 225 p.relative(bootstrapId.path, from: p.dirname(dartEntrypointId.path))); |
| 130 var entrypointJsContent = ''' | 226 var entrypointJsContent = new StringBuffer(''' |
| 131 var el = document.createElement("script"); | 227 var el; |
| 228 '''); | |
| 229 if (debugMode) { | |
| 230 entrypointJsContent.write(''' | |
| 231 el = document.createElement("script"); | |
| 232 el.defer = true; | |
| 233 el.async = false; | |
| 234 el.src = "dart_stack_trace_mapper.js"; | |
| 235 document.head.appendChild(el); | |
| 236 '''); | |
| 237 } | |
| 238 | |
| 239 entrypointJsContent.write(''' | |
| 240 el = document.createElement("script"); | |
| 132 el.defer = true; | 241 el.defer = true; |
| 133 el.async = false; | 242 el.async = false; |
| 134 el.src = "require.js"; | 243 el.src = "require.js"; |
| 135 el.setAttribute("data-main", "$bootstrapModuleName"); | 244 el.setAttribute("data-main", "$bootstrapModuleName"); |
| 136 document.head.appendChild(el); | 245 document.head.appendChild(el); |
| 137 '''; | 246 '''); |
|
Bob Nystrom
2017/05/22 20:21:44
What's going on here? Does it write this JS twice
| |
| 138 outputCompleters[jsEntrypointId] | 247 outputCompleters[jsEntrypointId].complete( |
| 139 .complete(new Asset.fromString(jsEntrypointId, entrypointJsContent)); | 248 new Asset.fromString(jsEntrypointId, entrypointJsContent.toString())); |
| 140 | 249 |
| 141 if (mode == BarbackMode.DEBUG) { | 250 if (debugMode) { |
| 142 outputCompleters[jsMapEntrypointId].complete(new Asset.fromString( | 251 outputCompleters[jsMapEntrypointId].complete(new Asset.fromString( |
| 143 jsMapEntrypointId, | 252 jsMapEntrypointId, |
| 144 '{"version":3,"sourceRoot":"","sources":[],"names":[],"mappings":"",' | 253 '{"version":3,"sourceRoot":"","sources":[],"names":[],"mappings":"",' |
| 145 '"file":""}')); | 254 '"file":""}')); |
| 146 } | 255 } |
| 147 }); | 256 }); |
| 148 } | 257 } |
| 149 | 258 |
| 150 /// Compiles [module] using the `dartdevc` binary from the SDK to a relative | 259 /// Compiles [module] using the `dartdevc` binary from the SDK to a relative |
| 151 /// path under the package that looks like `$outputDir/${module.id.name}.js`. | 260 /// path under the package that looks like `$outputDir/${module.id.name}.js`. |
| 152 /// | 261 /// |
| 153 /// Synchronously returns a `Map<AssetId, Future<Asset>>` so that you can know | 262 /// Synchronously returns a `Map<AssetId, Future<Asset>>` so that you can know |
| 154 /// immediately what assets will be output. | 263 /// immediately what assets will be output. |
| 155 Map<AssetId, Future<Asset>> createDartdevcModule( | 264 Map<AssetId, Future<Asset>> createDartdevcModule( |
| 156 AssetId id, | 265 AssetId id, |
| 157 ModuleReader moduleReader, | 266 ModuleReader moduleReader, |
| 158 ScratchSpace scratchSpace, | 267 ScratchSpace scratchSpace, |
| 159 Map<String, String> environmentConstants, | 268 Map<String, String> environmentConstants, |
| 160 BarbackMode mode, | 269 BarbackMode mode, |
| 161 logError(String message)) { | 270 logError(String message)) { |
| 162 assert(id.extension == '.js'); | 271 assert(id.extension == '.js'); |
| 163 var outputCompleters = <AssetId, Completer<Asset>>{ | 272 var outputCompleters = <AssetId, Completer<Asset>>{ |
| 164 id: new Completer(), | 273 id: new Completer(), |
| 165 }; | 274 }; |
| 166 if (mode == BarbackMode.DEBUG) { | 275 var debugMode = mode == BarbackMode.DEBUG; |
| 276 if (debugMode) { | |
| 167 outputCompleters[id.addExtension('.map')] = new Completer(); | 277 outputCompleters[id.addExtension('.map')] = new Completer(); |
| 168 } | 278 } |
| 169 | 279 |
| 170 return _ensureComplete(outputCompleters, () async { | 280 return _ensureComplete(outputCompleters, () async { |
| 171 var module = await moduleReader.moduleFor(id); | 281 var module = await moduleReader.moduleFor(id); |
| 172 if (module == null) return; | 282 if (module == null) return; |
| 173 var transitiveModuleDeps = await moduleReader.readTransitiveDeps(module); | 283 var transitiveModuleDeps = await moduleReader.readTransitiveDeps(module); |
| 174 var linkedSummaryIds = | 284 var linkedSummaryIds = |
| 175 transitiveModuleDeps.map((depId) => depId.linkedSummaryId).toSet(); | 285 transitiveModuleDeps.map((depId) => depId.linkedSummaryId).toSet(); |
| 176 var allAssetIds = new Set<AssetId>() | 286 var allAssetIds = new Set<AssetId>() |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 188 '--modules=amd', | 298 '--modules=amd', |
| 189 '--dart-sdk=${sdkDir.path}', | 299 '--dart-sdk=${sdkDir.path}', |
| 190 '--module-root=${scratchSpace.tempDir.path}', | 300 '--module-root=${scratchSpace.tempDir.path}', |
| 191 '--library-root=${p.dirname(jsOutputFile.path)}', | 301 '--library-root=${p.dirname(jsOutputFile.path)}', |
| 192 '--summary-extension=${linkedSummaryExtension.substring(1)}', | 302 '--summary-extension=${linkedSummaryExtension.substring(1)}', |
| 193 '--no-summarize', | 303 '--no-summarize', |
| 194 '-o', | 304 '-o', |
| 195 jsOutputFile.path, | 305 jsOutputFile.path, |
| 196 ]); | 306 ]); |
| 197 | 307 |
| 198 if (mode == BarbackMode.RELEASE) { | 308 if (debugMode) { |
| 309 request.arguments.addAll([ | |
| 310 '--source-map', | |
| 311 '--source-map-comment', | |
| 312 '--inline-source-map', | |
| 313 ]); | |
| 314 } else { | |
| 199 request.arguments.add('--no-source-map'); | 315 request.arguments.add('--no-source-map'); |
| 200 } | 316 } |
| 201 | 317 |
| 202 // Add environment constants. | 318 // Add environment constants. |
| 203 environmentConstants.forEach((key, value) { | 319 environmentConstants.forEach((key, value) { |
| 204 request.arguments.add('-D$key=$value'); | 320 request.arguments.add('-D$key=$value'); |
| 205 }); | 321 }); |
| 206 | 322 |
| 207 // Add all the linked summaries as summary inputs. | 323 // Add all the linked summaries as summary inputs. |
| 208 for (var id in linkedSummaryIds) { | 324 for (var id in linkedSummaryIds) { |
| (...skipping 23 matching lines...) Expand all Loading... | |
| 232 | 348 |
| 233 // TODO(jakemac53): Fix the ddc worker mode so it always sends back a bad | 349 // TODO(jakemac53): Fix the ddc worker mode so it always sends back a bad |
| 234 // status code if something failed. Today we just make sure there is an outp ut | 350 // status code if something failed. Today we just make sure there is an outp ut |
| 235 // JS file to verify it was successful. | 351 // JS file to verify it was successful. |
| 236 if (response.exitCode != EXIT_CODE_OK || !jsOutputFile.existsSync()) { | 352 if (response.exitCode != EXIT_CODE_OK || !jsOutputFile.existsSync()) { |
| 237 logError('Error compiling dartdevc module: ${module.id}.\n' | 353 logError('Error compiling dartdevc module: ${module.id}.\n' |
| 238 '${response.output}'); | 354 '${response.output}'); |
| 239 } else { | 355 } else { |
| 240 outputCompleters[module.id.jsId].complete( | 356 outputCompleters[module.id.jsId].complete( |
| 241 new Asset.fromBytes(module.id.jsId, jsOutputFile.readAsBytesSync())); | 357 new Asset.fromBytes(module.id.jsId, jsOutputFile.readAsBytesSync())); |
| 242 if (mode == BarbackMode.DEBUG) { | 358 if (debugMode) { |
| 243 var sourceMapFile = scratchSpace.fileFor(module.id.jsSourceMapId); | 359 var sourceMapFile = scratchSpace.fileFor(module.id.jsSourceMapId); |
| 244 outputCompleters[module.id.jsSourceMapId].complete(new Asset.fromBytes( | 360 outputCompleters[module.id.jsSourceMapId].complete(new Asset.fromBytes( |
| 245 module.id.jsSourceMapId, sourceMapFile.readAsBytesSync())); | 361 module.id.jsSourceMapId, sourceMapFile.readAsBytesSync())); |
| 246 } | 362 } |
| 247 } | 363 } |
| 248 }); | 364 }); |
| 249 } | 365 } |
| 250 | 366 |
| 251 /// Copies the `dart_sdk.js` and `require.js` AMD files from the SDK into | 367 /// Copies the `dart_sdk.js` and `require.js` AMD files from the SDK into |
| 252 /// [outputDir]. | 368 /// [outputDir]. |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 297 } finally { | 413 } finally { |
| 298 for (var id in completers.keys) { | 414 for (var id in completers.keys) { |
| 299 if (!completers[id].isCompleted) { | 415 if (!completers[id].isCompleted) { |
| 300 completers[id].completeError(new AssetNotFoundException(id)); | 416 completers[id].completeError(new AssetNotFoundException(id)); |
| 301 } | 417 } |
| 302 } | 418 } |
| 303 } | 419 } |
| 304 }(); | 420 }(); |
| 305 return futures; | 421 return futures; |
| 306 } | 422 } |
| OLD | NEW |