Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, 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 /** | 5 /** |
| 6 * This library contains semi-private APIs for implementing typed interfaces and | 6 * This library contains semi-private APIs for implementing typed interfaces and |
| 7 * exports. | 7 * exports. |
| 8 */ | 8 */ |
| 9 library js.mirrors; | 9 library js.mirrors; |
| 10 | 10 |
| 11 import 'dart:js' as js; | 11 import 'dart:js' as js; |
| 12 import 'dart:mirrors'; | 12 import 'dart:mirrors'; |
| 13 import 'dart:mirrors' as mirrors; | 13 import 'dart:mirrors' as mirrors; |
| 14 | 14 |
| 15 import 'package:js/src/metadata.dart'; | 15 import 'package:js/src/metadata.dart' as metadata; |
| 16 import 'package:js/src/js_elements.dart'; | 16 import 'package:js/src/js_elements.dart'; |
| 17 import 'package:js/src/js_impl.dart' as jsi; | 17 import 'package:js/src/js_impl.dart' as jsi; |
| 18 import 'package:js/src/js_impl.dart'; | 18 import 'package:js/src/js_impl.dart'; |
| 19 | 19 |
| 20 // This is the public interface of js.dart | 20 // This is the public interface of js.dart |
| 21 // The exports must match those in static.dart | 21 // The exports must match those in static.dart |
| 22 export 'package:js/src/js_impl.dart' hide JsInterface; | 22 // JsInterface is not include to show the version defined here |
| 23 export 'package:js/src/js_impl.dart' show JsGlobal, toJs, toDart, | |
| 24 registerJsConstructorForType, registerFactoryForJsConstructor; | |
| 23 export 'dart:js' show JsObject; | 25 export 'dart:js' show JsObject; |
| 24 export 'package:js/src/metadata.dart'; | 26 export 'package:js/src/metadata.dart'; |
| 25 | 27 |
| 26 import 'package:quiver/mirrors.dart'; | 28 import 'package:quiver/mirrors.dart'; |
| 27 | 29 |
| 28 final _dart = js.context['dart']; | 30 final _dart = js.context['dart']; |
| 29 final _obj = js.context['Object']; | 31 final _obj = js.context['Object']; |
| 30 | 32 |
| 31 // This is ugly. For each ExportedClass we need a reference to the class mirror | 33 // This is ugly. For each ExportedClass we need a reference to the class mirror |
| 32 // to implement constructors, but ExportedClass is used in code generation | 34 // to implement constructors, but ExportedClass is used in code generation |
| 33 // as well. | 35 // as well. |
| 34 Expando<ClassMirror> _classMirrors = new Expando<ClassMirror>(); | 36 Expando<ClassMirror> _classMirrors = new Expando<ClassMirror>(); |
| 35 | 37 |
| 36 initializeJavaScript() { | 38 initializeJavaScript() { |
| 37 var libraries = currentMirrorSystem().libraries; | 39 var libraries = currentMirrorSystem().libraries; |
| 38 | 40 |
| 39 var jsElements = new JsElements(); | 41 var jsElements = new JsElements(); |
| 40 var jsInterface = reflectType(JsInterface); | 42 var jsInterface = reflectType(JsInterface); |
| 41 | 43 |
| 42 for (var library in libraries.values) { | 44 for (var library in libraries.values) { |
| 43 var exportedLibrary; | 45 var exportedLibrary; |
| 44 var libraryName = _getName(library); | 46 var libraryName = _getName(library); |
| 45 var libraryExportAnnotation = _getExportAnnotation(library); | 47 var libraryExportAnnotation = _getExportAnnotation(library); |
| 46 if (libraryExportAnnotation != null) { | 48 if (libraryExportAnnotation != null) { |
| 47 exportedLibrary = jsElements.getLibrary(libraryName); | 49 exportedLibrary = jsElements.getLibrary(libraryName); |
| 48 } | 50 } |
| 49 | 51 |
| 50 var classes = library.declarations.values.where((d) => d is ClassMirror); | 52 var classes = library.declarations.values.where((d) => d is ClassMirror); |
| 51 for (ClassMirror clazz in classes) { | 53 for (ClassMirror clazz in classes) { |
| 52 JsProxy jsProxyAnnotation = _getJsProxyAnnotation(clazz); | 54 metadata.JsProxy jsProxyAnnotation = _getJsProxyAnnotation(clazz); |
| 53 if (jsProxyAnnotation != null && jsProxyAnnotation.constructor != null) { | 55 if (jsProxyAnnotation != null && jsProxyAnnotation.constructor != null) { |
| 54 _registerProxy(clazz, jsProxyAnnotation); | 56 _registerProxy(clazz, jsProxyAnnotation); |
| 55 } | 57 } |
| 56 | 58 |
| 57 var classExportAnnotation = _getExportAnnotation(clazz); | 59 var classExportAnnotation = _getExportAnnotation(clazz); |
| 58 var classNoExportAnnotation = _getNoExportAnnotation(clazz); | 60 var classNoExportAnnotation = _getNoExportAnnotation(clazz); |
| 59 | 61 |
| 60 if (classNoExportAnnotation == null | 62 if (classNoExportAnnotation == null |
| 61 && !clazz.isAbstract | 63 && !clazz.isAbstract |
| 62 && !classImplements(clazz, jsInterface) | 64 && !classImplements(clazz, jsInterface) |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 128 // remove the class name | 130 // remove the class name |
| 129 var name = rawName == c.name ? '' : rawName.substring(c.name.length + 1); | 131 var name = rawName == c.name ? '' : rawName.substring(c.name.length + 1); |
| 130 | 132 |
| 131 var parameters = ctor.parameters.map((p) => | 133 var parameters = ctor.parameters.map((p) => |
| 132 new ExportedParameter(_getName(p), _getKind(p), _getType(p))).toList(); | 134 new ExportedParameter(_getName(p), _getKind(p), _getType(p))).toList(); |
| 133 | 135 |
| 134 c.children[name] = new ExportedConstructor(name, c, parameters); | 136 c.children[name] = new ExportedConstructor(name, c, parameters); |
| 135 } | 137 } |
| 136 } | 138 } |
| 137 | 139 |
| 138 void _registerProxy(ClassMirror clazz, JsProxy jsProxyAnnotation) { | 140 void _registerProxy(ClassMirror clazz, metadata.JsProxy jsProxyAnnotation) { |
| 139 var constructorExpr = jsProxyAnnotation.constructor; | 141 var constructorExpr = jsProxyAnnotation.constructor; |
| 140 var createdConstructor = clazz.declarations[#created]; | 142 var createdConstructor = clazz.declarations[#created]; |
| 141 jsi.registerFactoryForJsConstructor( | 143 jsi.registerFactoryForJsConstructor( |
| 142 getPath(jsProxyAnnotation.constructor), | 144 getPath(jsProxyAnnotation.constructor), |
| 143 (JsObject o) => clazz.newInstance(#created, [o]).reflectee); | 145 (JsObject o) => clazz.newInstance(#created, [o]).reflectee); |
| 144 } | 146 } |
| 145 | 147 |
| 146 _exportLibrary(ExportedLibrary library, JsObject parent) { | 148 _exportLibrary(ExportedLibrary library, JsObject parent) { |
| 147 JsObject libraryJsObj = parent; | 149 JsObject libraryJsObj = parent; |
| 148 var parts = library.name.split('.'); | 150 var parts = library.name.split('.'); |
| 149 parts.forEach((p) { | 151 parts.forEach((p) { |
| 150 if (!libraryJsObj.hasProperty(p)) { | 152 if (!libraryJsObj.hasProperty(p)) { |
| 151 libraryJsObj = libraryJsObj[p] = new JsObject.jsify({}); | 153 libraryJsObj = libraryJsObj[p] = new JsObject(_obj); |
| 152 } else { | 154 } else { |
| 153 libraryJsObj = libraryJsObj[p]; | 155 libraryJsObj = libraryJsObj[p]; |
| 154 } | 156 } |
| 155 }); | 157 }); |
| 156 library.declarations.forEach((n, d) => _exportDeclaration(d, libraryJsObj)); | 158 library.declarations.forEach((n, d) => _exportDeclaration(d, libraryJsObj)); |
| 157 } | 159 } |
| 158 | 160 |
| 159 _exportDeclaration(ExportedElement e, JsObject parent) { | 161 _exportDeclaration(ExportedElement e, JsObject parent) { |
| 160 if (e is ExportedClass) { | 162 if (e is ExportedClass) { |
| 161 _exportClass(e, parent); | 163 _exportClass(e, parent); |
| (...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 276 call() => throw new StateError('There should always been at least 1 parameter' | 278 call() => throw new StateError('There should always been at least 1 parameter' |
| 277 '(js this).'); | 279 '(js this).'); |
| 278 | 280 |
| 279 noSuchMethod(Invocation invocation) { | 281 noSuchMethod(Invocation invocation) { |
| 280 var self = invocation.positionalArguments.first; | 282 var self = invocation.positionalArguments.first; |
| 281 var args = invocation.positionalArguments.skip(1).toList(); | 283 var args = invocation.positionalArguments.skip(1).toList(); |
| 282 return f(self, args); | 284 return f(self, args); |
| 283 } | 285 } |
| 284 } | 286 } |
| 285 | 287 |
| 286 Export _getExportAnnotation(DeclarationMirror d) { | 288 metadata.Export _getExportAnnotation(DeclarationMirror d) { |
| 287 var m = d.metadata.firstWhere((m) => m.type.reflectedType == Export, | 289 var m = d.metadata |
| 288 orElse: () => null); | 290 .firstWhere((m) => m.type.reflectedType == metadata.Export, |
| 291 orElse: () => null); | |
| 289 return m == null ? null : m.reflectee; | 292 return m == null ? null : m.reflectee; |
| 290 } | 293 } |
| 291 | 294 |
| 292 NoExport _getNoExportAnnotation(DeclarationMirror d) { | 295 metadata.NoExport _getNoExportAnnotation(DeclarationMirror d) { |
| 293 var m = d.metadata.firstWhere((m) => m.type.reflectedType == NoExport, | 296 var m = d.metadata |
| 294 orElse: () => null); | 297 .firstWhere((m) => m.type.reflectedType == metadata.NoExport, |
| 298 orElse: () => null); | |
| 295 return m == null ? null : m.reflectee; | 299 return m == null ? null : m.reflectee; |
| 296 } | 300 } |
| 297 | 301 |
| 298 String _getName(DeclarationMirror m) => MirrorSystem.getName(m.simpleName); | 302 String _getName(DeclarationMirror m) => MirrorSystem.getName(m.simpleName); |
| 299 | 303 |
| 300 ParameterKind _getKind(ParameterMirror p) { | 304 ParameterKind _getKind(ParameterMirror p) { |
| 301 if (p.isNamed) return ParameterKind.NAMED; | 305 if (p.isNamed) return ParameterKind.NAMED; |
| 302 if (p.isOptional) return ParameterKind.POSITIONAL; | 306 if (p.isOptional) return ParameterKind.POSITIONAL; |
| 303 return ParameterKind.REQUIRED; | 307 return ParameterKind.REQUIRED; |
| 304 } | 308 } |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 343 } | 347 } |
| 344 | 348 |
| 345 if (jsConstructor != null) { | 349 if (jsConstructor != null) { |
| 346 // TODO: support constructor expressions | 350 // TODO: support constructor expressions |
| 347 var ctor = getPath(jsConstructor); | 351 var ctor = getPath(jsConstructor); |
| 348 var jsObj = new JsObject(ctor, args); | 352 var jsObj = new JsObject(ctor, args); |
| 349 return classMirror.newInstance(#created, [jsObj]).reflectee; | 353 return classMirror.newInstance(#created, [jsObj]).reflectee; |
| 350 } | 354 } |
| 351 } | 355 } |
| 352 | 356 |
| 353 dynamic noSuchMethod(Invocation i) { | 357 dynamic noSuchMethod(Invocation invocation) { |
| 354 var mirror = mirrors.reflect(this); | 358 var mirror = mirrors.reflect(this); |
| 355 var decl = getDeclaration(mirror.type, i.memberName); | 359 var decl = getDeclaration(mirror.type, invocation.memberName); |
| 356 | 360 |
| 357 if (decl != null) { | 361 if (decl != null) { |
| 358 mirrors.MethodMirror method = decl; | 362 mirrors.MethodMirror method = decl; |
| 359 String name = mirrors.MirrorSystem.getName(i.memberName); | 363 String name = mirrors.MirrorSystem.getName(invocation.memberName); |
| 360 if (i.isGetter) { | 364 if (invocation.isGetter) { |
| 361 var o = toDart(toJs(this)[name]); | 365 var o = toDart(toJs(this)[name]); |
| 362 assert(o == null || | 366 assert(o == null || |
| 363 mirrors.reflect(o).type.isSubtypeOf(method.returnType)); | 367 mirrors.reflect(o).type.isSubtypeOf(method.returnType)); |
| 364 return o; | 368 return o; |
| 365 } | 369 } |
| 366 if (i.isSetter) { | 370 if (invocation.isSetter) { |
| 367 // remove the trailing '=' from the setter name | 371 // remove the trailing '=' from the setter name |
| 368 name = name.substring(0, name.length - 1); | 372 name = name.substring(0, name.length - 1); |
| 369 var v = toJs(i.positionalArguments[0]); | 373 var v = toJs(invocation.positionalArguments[0]); |
| 370 toJs(this)[name] = v; | 374 toJs(this)[name] = v; |
| 371 return null; | 375 return null; |
| 372 } | 376 } |
| 373 if (i.isMethod) { | 377 if (invocation.isMethod) { |
| 374 var jsArgs = i.positionalArguments.map(toJs).toList(); | 378 MethodMirror m = decl; |
| 375 var o = toDart(toJs(this).callMethod(name, jsArgs)); | 379 var positionalParams = m.parameters.where((p) => !p.isNamed).toList(); |
| 380 var positionalArgs = invocation.positionalArguments; | |
| 381 var jsArgs = new List(positionalArgs.length); | |
| 382 for (int i = 0; i < positionalArgs.length; i++) { | |
| 383 var param = positionalParams[i]; | |
| 384 var arg = positionalArgs[i]; | |
| 385 var hasJsify = param.metadata.any((m) => m.reflectee == metadata.jsify ); | |
|
Siggi Cherem (dart-lang)
2014/10/07 20:41:41
80
justinfagnani
2014/10/08 06:30:02
Done.
| |
| 386 if (hasJsify) { | |
| 387 jsArgs[i] = jsify(arg); | |
| 388 } else { | |
| 389 jsArgs[i] = toJs(arg); | |
| 390 } | |
| 391 } | |
| 392 var returnType = m.returnType.hasReflectedType | |
| 393 ? m.returnType.originalDeclaration.simpleName | |
| 394 : null; | |
| 395 var o = toDart(toJs(this).callMethod(name, jsArgs), returnType); | |
| 376 assert(o == null || | 396 assert(o == null || |
| 377 mirrors.reflect(o).type.isSubtypeOf(method.returnType)); | 397 mirrors.reflect(o).type.isSubtypeOf(method.returnType)); |
| 378 return o; | 398 return o; |
| 379 } | 399 } |
| 380 assert(false); | 400 assert(false); |
| 381 } | 401 } |
| 382 return super.noSuchMethod(i); | 402 return super.noSuchMethod(invocation); |
| 383 } | 403 } |
| 384 } | 404 } |
| 385 | 405 |
| 386 JsProxy _getJsProxyAnnotation(ClassMirror c) { | 406 metadata.JsProxy _getJsProxyAnnotation(ClassMirror c) { |
| 387 var jsProxyAnnotationMirror = | 407 var jsProxyAnnotationMirror = c.metadata |
| 388 c.metadata | 408 .firstWhere((i) => i.reflectee is metadata.JsProxy, orElse: () => null); |
| 389 .firstWhere((i) => i.reflectee is JsProxy, orElse: () => null); | |
| 390 | 409 |
| 391 if (jsProxyAnnotationMirror == null) return null; | 410 if (jsProxyAnnotationMirror == null) return null; |
| 392 | 411 |
| 393 return jsProxyAnnotationMirror.reflectee; | 412 return jsProxyAnnotationMirror.reflectee; |
| 394 } | 413 } |
| 395 | 414 |
| 396 | 415 |
| 397 /** | 416 /** |
| 398 * Returns all the declarations on [classMirror] and its super classes and | 417 * Returns all the declarations on [classMirror] and its super classes and |
| 399 * interfaces. This is difference from [ClassMirror.instanceMembers]. | 418 * interfaces. This is difference from [ClassMirror.instanceMembers]. |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 413 _getDeclarations(classMirror.superclass, declarations); | 432 _getDeclarations(classMirror.superclass, declarations); |
| 414 } | 433 } |
| 415 // TODO: See if a getter can shadow an implicit setter from a field in a | 434 // TODO: See if a getter can shadow an implicit setter from a field in a |
| 416 // superclass. This could happen if a superclass has a field and a subclass | 435 // superclass. This could happen if a superclass has a field and a subclass |
| 417 // has a getter with the same name. Since the field doesn't induce a setter | 436 // has a getter with the same name. Since the field doesn't induce a setter |
| 418 // in the declarations, the field would be replaced by the getter and there | 437 // in the declarations, the field would be replaced by the getter and there |
| 419 // would be no associated setter. The solution would be to create a synthetic | 438 // would be no associated setter. The solution would be to create a synthetic |
| 420 // setter if we're adding a getter than shadows a field. | 439 // setter if we're adding a getter than shadows a field. |
| 421 declarations.addAll(classMirror.declarations); | 440 declarations.addAll(classMirror.declarations); |
| 422 } | 441 } |
| OLD | NEW |