OLD | NEW |
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, 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 library dev_compiler.src.codegen.js_codegen; | 5 library dev_compiler.src.codegen.js_codegen; |
6 | 6 |
7 import 'dart:collection' show HashSet, HashMap; | 7 import 'dart:collection' show HashSet, HashMap; |
8 | 8 |
9 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; | 9 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; |
10 import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator; | 10 import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator; |
(...skipping 256 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
267 var type = node.element.type; | 267 var type = node.element.type; |
268 if (_pendingClasses.remove(node.element) == null) return null; | 268 if (_pendingClasses.remove(node.element) == null) return null; |
269 | 269 |
270 var name = type.name; | 270 var name = type.name; |
271 var result = js.statement('let # = dart.typedef(#, () => #);', [ | 271 var result = js.statement('let # = dart.typedef(#, () => #);', [ |
272 new JS.Identifier(name), | 272 new JS.Identifier(name), |
273 js.string(name, "'"), | 273 js.string(name, "'"), |
274 _emitTypeName(node.element.type, lowerTypedef: true) | 274 _emitTypeName(node.element.type, lowerTypedef: true) |
275 ]); | 275 ]); |
276 | 276 |
277 return _finishClassDef(type, result); | 277 return _finishClassDef(type, result, null); |
278 } | 278 } |
279 | 279 |
280 @override | 280 @override |
281 JS.Expression visitTypeName(TypeName node) => _emitTypeName(node.type); | 281 JS.Expression visitTypeName(TypeName node) => _emitTypeName(node.type); |
282 | 282 |
283 @override | 283 @override |
284 JS.Statement visitClassTypeAlias(ClassTypeAlias node) { | 284 JS.Statement visitClassTypeAlias(ClassTypeAlias node) { |
285 // If we've already emitted this class, skip it. | 285 // If we've already emitted this class, skip it. |
286 var type = node.element.type; | 286 var type = node.element.type; |
287 if (_pendingClasses.remove(node.element) == null) return null; | 287 if (_pendingClasses.remove(node.element) == null) return null; |
288 | 288 |
289 var name = node.name.name; | 289 var name = node.name.name; |
290 var heritage = | 290 var heritage = |
291 js.call('dart.mixin(#)', [_visitList(node.withClause.mixinTypes)]); | 291 js.call('dart.mixin(#)', [_visitList(node.withClause.mixinTypes)]); |
292 var classDecl = new JS.ClassDeclaration( | 292 var classDecl = new JS.ClassDeclaration( |
293 new JS.ClassExpression(new JS.Identifier(name), heritage, [])); | 293 new JS.ClassExpression(new JS.Identifier(name), heritage, [])); |
294 | 294 |
295 return _finishClassDef(type, classDecl); | 295 return _finishClassDef(type, classDecl, null); |
296 } | 296 } |
297 | 297 |
298 JS.Statement _emitJsType(String dartClassName, DartObjectImpl jsName) { | 298 JS.Statement _emitJsType(String dartClassName, DartObjectImpl jsName) { |
299 var jsTypeName = getConstantField(jsName, 'name', types.stringType); | 299 var jsTypeName = getConstantField(jsName, 'name', types.stringType); |
300 | 300 |
301 if (jsTypeName != null && jsTypeName != dartClassName) { | 301 if (jsTypeName != null && jsTypeName != dartClassName) { |
302 // We export the JS type as if it was a Dart type. For example this allows | 302 // We export the JS type as if it was a Dart type. For example this allows |
303 // `dom.InputElement` to actually be HTMLInputElement. | 303 // `dom.InputElement` to actually be HTMLInputElement. |
304 // TODO(jmesserly): if we had the JsName on the Element, we could just | 304 // TODO(jmesserly): if we had the JsName on the Element, we could just |
305 // generate it correctly when we refer to it. | 305 // generate it correctly when we refer to it. |
306 if (isPublic(dartClassName)) _addExport(dartClassName); | 306 if (isPublic(dartClassName)) _addExport(dartClassName); |
307 return js.statement('let # = #;', [dartClassName, jsTypeName]); | 307 return js.statement('let # = #;', [dartClassName, jsTypeName]); |
308 } | 308 } |
309 return null; | 309 return null; |
310 } | 310 } |
311 | 311 |
312 @override | 312 @override |
313 JS.Statement visitClassDeclaration(ClassDeclaration node) { | 313 JS.Statement visitClassDeclaration(ClassDeclaration node) { |
314 // If we've already emitted this class, skip it. | 314 // If we've already emitted this class, skip it. |
315 var classElem = node.element; | 315 var classElem = node.element; |
316 var type = classElem.type; | 316 var type = classElem.type; |
317 if (_pendingClasses.remove(classElem) == null) return null; | 317 if (_pendingClasses.remove(classElem) == null) return null; |
318 | 318 |
319 var jsName = getAnnotationValue(node, _isJsNameAnnotation); | 319 var jsName = getAnnotationValue(node, _isJsNameAnnotation); |
| 320 |
320 if (jsName != null) return _emitJsType(node.name.name, jsName); | 321 if (jsName != null) return _emitJsType(node.name.name, jsName); |
321 | 322 |
322 var ctors = <ConstructorDeclaration>[]; | 323 var ctors = <ConstructorDeclaration>[]; |
323 var fields = <FieldDeclaration>[]; | 324 var fields = <FieldDeclaration>[]; |
324 var staticFields = <FieldDeclaration>[]; | 325 var staticFields = <FieldDeclaration>[]; |
325 for (var member in node.members) { | 326 for (var member in node.members) { |
326 if (member is ConstructorDeclaration) { | 327 if (member is ConstructorDeclaration) { |
327 ctors.add(member); | 328 ctors.add(member); |
328 } else if (member is FieldDeclaration) { | 329 } else if (member is FieldDeclaration) { |
329 (member.isStatic ? staticFields : fields).add(member); | 330 (member.isStatic ? staticFields : fields).add(member); |
330 } | 331 } |
331 } | 332 } |
332 | 333 |
333 var classExpr = new JS.ClassExpression(new JS.Identifier(type.name), | 334 var classExpr = new JS.ClassExpression(new JS.Identifier(type.name), |
334 _classHeritage(node), _emitClassMethods(node, ctors, fields)); | 335 _classHeritage(node), _emitClassMethods(node, ctors, fields)); |
335 | 336 |
336 var body = | 337 var body = |
337 _finishClassMembers(classElem, classExpr, ctors, fields, staticFields); | 338 _finishClassMembers(classElem, classExpr, ctors, fields, staticFields); |
338 | 339 |
339 return _finishClassDef(type, body); | 340 var jsPeer = getAnnotationValue(node, _isJsPeerInterface); |
| 341 String jsPeerName = null; |
| 342 if (jsPeer != null) { |
| 343 jsPeerName = getConstantField(jsPeer, 'name', types.stringType); |
| 344 } |
| 345 |
| 346 return _finishClassDef(type, body, jsPeerName); |
340 } | 347 } |
341 | 348 |
342 @override | 349 @override |
343 JS.Statement visitEnumDeclaration(EnumDeclaration node) => | 350 JS.Statement visitEnumDeclaration(EnumDeclaration node) => |
344 _unimplementedCall("Unimplemented enum: $node").toStatement(); | 351 _unimplementedCall("Unimplemented enum: $node").toStatement(); |
345 | 352 |
346 /// Given a class element and body, complete the class declaration. | 353 /// Given a class element and body, complete the class declaration. |
347 /// This handles generic type parameters, laziness (in library-cycle cases), | 354 /// This handles generic type parameters, laziness (in library-cycle cases), |
348 /// and ensuring dependencies are loaded first. | 355 /// and ensuring dependencies are loaded first. |
349 JS.Statement _finishClassDef(ParameterizedType type, JS.Statement body) { | 356 JS.Statement _finishClassDef( |
| 357 ParameterizedType type, JS.Statement body, String jsPeerName) { |
350 var name = type.name; | 358 var name = type.name; |
351 var genericName = '$name\$'; | 359 var genericName = '$name\$'; |
352 | 360 |
353 JS.Statement genericDef; | 361 JS.Statement genericDef; |
354 JS.Expression genericInst; | 362 JS.Expression genericInst; |
355 if (type.typeParameters.isNotEmpty) { | 363 if (type.typeParameters.isNotEmpty) { |
356 genericDef = _emitGenericClassDef(type, body); | 364 genericDef = _emitGenericClassDef(type, body); |
357 var target = genericName; | 365 var target = genericName; |
358 if (_needQualifiedName(type.element)) { | 366 if (_needQualifiedName(type.element)) { |
359 target = js.call('#.#', [_exportsVar, genericName]); | 367 target = js.call('#.#', [_exportsVar, genericName]); |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
398 var classDefs = <JS.Statement>[]; | 406 var classDefs = <JS.Statement>[]; |
399 if (type is InterfaceType) { | 407 if (type is InterfaceType) { |
400 _emitClassIfNeeded(classDefs, type.superclass); | 408 _emitClassIfNeeded(classDefs, type.superclass); |
401 for (var m in type.element.mixins) { | 409 for (var m in type.element.mixins) { |
402 _emitClassIfNeeded(classDefs, m); | 410 _emitClassIfNeeded(classDefs, m); |
403 } | 411 } |
404 } else if (type is FunctionType) { | 412 } else if (type is FunctionType) { |
405 _emitClassIfNeeded(classDefs, types.functionType); | 413 _emitClassIfNeeded(classDefs, types.functionType); |
406 } | 414 } |
407 classDefs.add(body); | 415 classDefs.add(body); |
| 416 if (jsPeerName != null) { |
| 417 classDefs.add(js.statement( |
| 418 'dart.copyProperties(dart.global.#.prototype, #.prototype);', [ |
| 419 _propertyName(jsPeerName), |
| 420 name |
| 421 ])); |
| 422 } |
408 return _statement(classDefs); | 423 return _statement(classDefs); |
409 } | 424 } |
410 | 425 |
411 void _emitClassIfNeeded(List<JS.Statement> defs, DartType base) { | 426 void _emitClassIfNeeded(List<JS.Statement> defs, DartType base) { |
412 // We can only emit classes from this library. | 427 // We can only emit classes from this library. |
413 if (base.element.library != currentLibrary) return; | 428 if (base.element.library != currentLibrary) return; |
414 | 429 |
415 var baseNode = _pendingClasses[base.element]; | 430 var baseNode = _pendingClasses[base.element]; |
416 if (baseNode != null) defs.add(visitClassDeclaration(baseNode)); | 431 if (baseNode != null) defs.add(visitClassDeclaration(baseNode)); |
417 } | 432 } |
(...skipping 1908 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2326 JS.Expression _extensionMethodName(String name, LibraryElement library) { | 2341 JS.Expression _extensionMethodName(String name, LibraryElement library) { |
2327 var extName = '\$$name'; | 2342 var extName = '\$$name'; |
2328 if (library == currentLibrary) { | 2343 if (library == currentLibrary) { |
2329 // TODO(jacobr): need to do a better job ensuring that extension method | 2344 // TODO(jacobr): need to do a better job ensuring that extension method |
2330 // name symbols do not conflict with other symbols before we can let | 2345 // name symbols do not conflict with other symbols before we can let |
2331 // user defined libraries define extension methods. | 2346 // user defined libraries define extension methods. |
2332 if (_extensionMethodNames.add(extName)) { | 2347 if (_extensionMethodNames.add(extName)) { |
2333 _pendingSymbols.add(new JS.Identifier(extName)); | 2348 _pendingSymbols.add(new JS.Identifier(extName)); |
2334 _addExport(extName); | 2349 _addExport(extName); |
2335 } | 2350 } |
| 2351 return new JS.Identifier(extName); |
2336 } | 2352 } |
2337 return js.call('#.#', [_libraryName(library), _propertyName(extName)]); | 2353 return js.call('#.#', [_libraryName(library), _propertyName(extName)]); |
2338 } | 2354 } |
2339 | 2355 |
2340 bool _externalOrNative(node) => | 2356 bool _externalOrNative(node) => |
2341 node.externalKeyword != null || _functionBody(node) is NativeFunctionBody; | 2357 node.externalKeyword != null || _functionBody(node) is NativeFunctionBody; |
2342 | 2358 |
2343 FunctionBody _functionBody(node) => | 2359 FunctionBody _functionBody(node) => |
2344 node is FunctionDeclaration ? node.functionExpression.body : node.body; | 2360 node is FunctionDeclaration ? node.functionExpression.body : node.body; |
2345 | 2361 |
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2418 assert(uri.scheme == 'package'); | 2434 assert(uri.scheme == 'package'); |
2419 // filepath is good here, we want the output to start with a directory | 2435 // filepath is good here, we want the output to start with a directory |
2420 // matching the package name. | 2436 // matching the package name. |
2421 } | 2437 } |
2422 return filepath; | 2438 return filepath; |
2423 } | 2439 } |
2424 | 2440 |
2425 // TODO(jmesserly): validate the library. See issue #135. | 2441 // TODO(jmesserly): validate the library. See issue #135. |
2426 bool _isJsNameAnnotation(DartObjectImpl value) => value.type.name == 'JsName'; | 2442 bool _isJsNameAnnotation(DartObjectImpl value) => value.type.name == 'JsName'; |
2427 | 2443 |
| 2444 bool _isJsPeerInterface(DartObjectImpl value) => |
| 2445 value.type.name == 'JsPeerInterface'; |
| 2446 |
2428 // TODO(jacobr): we would like to do something like the following | 2447 // TODO(jacobr): we would like to do something like the following |
2429 // but we don't have summary support yet. | 2448 // but we don't have summary support yet. |
2430 // bool _supportJsExtensionMethod(AnnotatedNode node) => | 2449 // bool _supportJsExtensionMethod(AnnotatedNode node) => |
2431 // _getAnnotation(node, "SupportJsExtensionMethod") != null; | 2450 // _getAnnotation(node, "SupportJsExtensionMethod") != null; |
OLD | NEW |