Skip to content
This repository has been archived by the owner on Feb 22, 2018. It is now read-only.

Commit

Permalink
Remove mirrors imports in transformed code.
Browse files Browse the repository at this point in the history
  • Loading branch information
justinfagnani committed Sep 30, 2014
1 parent df28c17 commit cb2da41
Show file tree
Hide file tree
Showing 20 changed files with 159 additions and 68 deletions.
11 changes: 3 additions & 8 deletions lib/js.dart
Expand Up @@ -8,11 +8,6 @@
*/
library js;

export 'dart:js' show JsObject;

// this must be a package import due to dartbug.com/20666
export 'package:js/src/js_impl.dart' show JsInterface;
export 'package:js/src/metadata.dart';
import 'package:js/src/mirrors.dart' as impl show initializeJavaScript;

void initializeJavaScript() => impl.initializeJavaScript();
// js.dart is just an alias for mirrors.dart at runtime. The transformer
// replaces this import of js.dart with src/static.dart.
export 'package:js/src/mirrors.dart';
24 changes: 11 additions & 13 deletions lib/src/js_impl.dart
Expand Up @@ -11,24 +11,20 @@ library js.impl;
import 'dart:js';
export 'dart:js' show context, JsObject;

// TODO(justinfagnani): replace this import with a static impl
// during transformation
import 'package:js/src/mirrors.dart';
import 'package:js/src/mirrors.dart' as mirrors show isGlobal;

const DART_OBJECT_PROPERTY = '__dart_object__';

/**
* The base class of Dart interfaces for JavaScript objects.
*/
abstract class JsInterface extends JsInterfaceImpl {
abstract class JsInterface {

final JsObject _jsObject;

factory JsInterface(Type type, [Iterable args]) =>
new JsInterfaceImpl(type, args);
factory JsInterface(Type type, [Iterable args]) {
throw new StateError('JsInterface should never be called');
}

JsInterface.created(JsObject o) : _jsObject = o, super.created() {
JsInterface.created(JsObject o) : _jsObject = o {
// since multiple Dart objects of different classes may represent
// the global name space, we don't store a reference
if (!isGlobal(this)) {
Expand All @@ -41,10 +37,12 @@ abstract class JsInterface extends JsInterfaceImpl {

}

// This only works in JS because we're still pulling mirrors. We need to
// generate metadata about which classes are global when we transform out
// mirrors.
bool isGlobal(JsInterface o) => mirrors.isGlobal(o);
/**
* Marker interface for global objects.
*/
abstract class JsGlobal {}

bool isGlobal(JsInterface o) => o is JsGlobal;

/**
* Converts a Dart object to a [JsObject] (or supported primitive) for sending
Expand Down
20 changes: 10 additions & 10 deletions lib/src/mirrors.dart
Expand Up @@ -12,11 +12,17 @@ import 'dart:js' as js;
import 'dart:mirrors';
import 'dart:mirrors' as mirrors;

import 'package:js/js.dart';
import 'package:js/src/metadata.dart';
import 'package:js/src/js_elements.dart';
import 'package:js/src/js_impl.dart' as jsi;
import 'package:js/src/js_impl.dart';

// This is the public interface of js.dart
// The exports must match those in static.dart
export 'package:js/src/js_impl.dart' hide JsInterface;
export 'dart:js' show JsObject;
export 'package:js/src/metadata.dart';

import 'package:quiver/mirrors.dart';

final _dart = js.context['dart'];
Expand Down Expand Up @@ -270,17 +276,11 @@ DartType _getType(ParameterMirror p) => new DartType(_getName(p.type));

final Expando _globals = new Expando();

bool isGlobal(JsInterface o) {
var classMirror = reflect(o).type;
var jsProxyAnnotation = _getJsProxyAnnotation(classMirror);
return jsProxyAnnotation.global;
}

class JsInterfaceImpl {
class JsInterface extends jsi.JsInterface {

JsInterfaceImpl.created();
JsInterface.created(JsObject o) : super.created(o);

factory JsInterfaceImpl(Type type, Iterable args) {
factory JsInterface(Type type, Iterable args) {
if (_globals[type] != null) {
return _globals[type];
}
Expand Down
15 changes: 15 additions & 0 deletions lib/src/static.dart
@@ -0,0 +1,15 @@
library js.static;

// This is the public interface of js.dart
// The exports must match those in static.dart
export 'package:js/src/js_impl.dart';
export 'dart:js' show JsObject;
export 'package:js/src/metadata.dart';

void initializeJavaScript() {
throw new StateError("Non-transformed initialization is being called. "
"You are using js.dart in deployed mode, but you probably haven't "
"configured the initializer transformer, or you are calling "
"initializeJavaScript() somewhere other than main(). "
"Try adding '- js/initializer' to your applications transformers.");
}
5 changes: 1 addition & 4 deletions lib/src/transformer/js_proxy_generator.dart
Expand Up @@ -44,10 +44,7 @@ class JsProxyGenerator {
this.transaction)
: library = library,
jsLibrary = jsLibrary,
jsInterfaceClass = jsLibrary
.exportedLibraries
.singleWhere((l) => l.name == 'js.impl')
.getType('JsInterface'),
jsInterfaceClass = getImplLib(jsLibrary).getType('JsInterface'),
jsProxyClass = jsMetadataLibrary.getType('JsProxy'),
exportClass = jsMetadataLibrary.getType('Export'),
noExportClass = jsMetadataLibrary.getType('NoExport'),
Expand Down
32 changes: 32 additions & 0 deletions lib/src/transformer/remove_mirrors_transformer.dart
@@ -0,0 +1,32 @@
// Copyright (c) 2014, 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 js.transformer.remove_mirrors_transformer;

import 'package:barback/barback.dart' show Asset, AssetId, BarbackSettings,
Transform, Transformer;
import 'package:logging/logging.dart' show Logger;

final _logger = new Logger('js.transformer.remove_mirrors_transformer');

class RemoveMirrorsTransformer extends Transformer {

RemoveMirrorsTransformer();

RemoveMirrorsTransformer.asPlugin(BarbackSettings settings);

@override
isPrimary(AssetId id) => id.package == 'js' && id.path == 'lib/js.dart';

@override
apply(Transform transform) {
var input = transform.primaryInput;
return input.readAsString().then((source) {
var newSource = source.replaceAll(
"export 'package:js/src/mirrors.dart';",
"export 'package:js/src/static.dart';");
transform.addOutput(new Asset.fromString(input.id, newSource));
});
}
}
5 changes: 1 addition & 4 deletions lib/src/transformer/scanning_visitor.dart
Expand Up @@ -48,10 +48,7 @@ class ScanningVisitor extends RecursiveElementVisitor {
LibraryElement jsMetadataLibrary,
this.entryLibrary)
: this.jsLibrary = jsLibrary,
jsInterfaceClass = jsLibrary
.exportedLibraries
.singleWhere((l) => l.name == 'js.impl')
.getType('JsInterface'),
jsInterfaceClass = getImplLib(jsLibrary).getType('JsInterface'),
jsProxyClass = jsMetadataLibrary.getType('JsProxy'),
exportClass = jsMetadataLibrary.getType('Export'),
noExportClass = jsMetadataLibrary.getType('NoExport') {
Expand Down
14 changes: 14 additions & 0 deletions lib/src/transformer/utils.dart
Expand Up @@ -14,6 +14,20 @@ import 'package:quiver/iterables.dart' show max;
const String DART_INITIALIZER_SUFFIX = "__init_js__.dart";
const String JS_INITIALIZER_SUFFIX = "__init_js__.js";

LibraryElement getEnvironementImplLib(LibraryElement jsLib) =>
jsLib.exportedLibraries
.singleWhere((l) => l.name == 'js.mirrors' || l.name == 'js.static');

LibraryElement getMetadataLib(LibraryElement jsLib) =>
getEnvironementImplLib(jsLib)
.exportedLibraries
.singleWhere((l) => l.name == 'js.metadata');

LibraryElement getImplLib(LibraryElement jsLib) =>
getEnvironementImplLib(jsLib)
.exportedLibraries
.singleWhere((l) => l.name == 'js.impl');

JsProxy getProxyAnnotation(ClassElement interface, ClassElement jsProxyClass) {
var node = interface.node;
for (Annotation a in node.metadata) {
Expand Down
2 changes: 2 additions & 0 deletions pubspec.yaml
Expand Up @@ -17,3 +17,5 @@ dependencies:
dev_dependencies:
path: '>=1.0.0 <2.0.0'
unittest: '>=0.9.0 <0.12.0'
transformers:
- js/src/transformer/remove_mirrors_transformer
5 changes: 2 additions & 3 deletions test/transformer/dart_initializer_generator_test.dart
Expand Up @@ -9,6 +9,7 @@ import 'package:analyzer/src/generated/engine.dart';
import 'package:barback/barback.dart';
import 'package:js/src/transformer/dart_initializer_generator.dart';
import 'package:js/src/transformer/scanning_visitor.dart';
import 'package:js/src/transformer/utils.dart';
import 'package:source_maps/refactor.dart';
import 'package:source_span/source_span.dart';
import 'package:unittest/unittest.dart';
Expand All @@ -29,9 +30,7 @@ main() {
var analyserInfo = initAnalyzer();
testLib = analyserInfo.testLib;
jsLib = analyserInfo.jsLib;
jsMetadataLib = jsLib
.exportedLibraries
.singleWhere((l) => l.name == 'js.metadata');
jsMetadataLib = getMetadataLib(jsLib);
testLibSource = analyserInfo.context.getContents(testLib.source).data;
});

Expand Down
2 changes: 2 additions & 0 deletions test/transformer/entry_point_transformer_test.dart
Expand Up @@ -26,6 +26,8 @@ main() {
'test|lib/library.dart': readTestFile('library.dart'),
'test|web/entry_point.dart': readTestFile('entry_point.dart.test'),
'js|lib/js.dart': readJsPackageFile('js.dart'),
'js|lib/src/mirrors.dart': readJsPackageFile('src/mirrors.dart'),
'js|lib/src/static.dart': readJsPackageFile('src/static.dart'),
'js|lib/src/js_impl.dart': readJsPackageFile('src/js_impl.dart'),
'js|lib/src/metadata.dart': readJsPackageFile('src/metadata.dart'),
}, null);
Expand Down
7 changes: 3 additions & 4 deletions test/transformer/js_proxy_generator_test.dart
Expand Up @@ -6,13 +6,14 @@ library js.test.transformer.proxy_generator_test;

import 'package:analyzer/src/generated/element.dart';
import 'package:analyzer/src/generated/engine.dart';
import 'package:barback/barback.dart';
import 'package:js/src/transformer/js_proxy_generator.dart';
import 'package:js/src/transformer/utils.dart';
import 'package:source_maps/refactor.dart';
import 'package:source_span/source_span.dart';
import 'package:unittest/unittest.dart';

import 'utils.dart';
import 'package:barback/barback.dart';

main() {

Expand All @@ -38,9 +39,7 @@ main() {
testLib.getType('ContextImpl'),
testLib.getType('JsFooImpl'),
testLib.getType('JsBarImpl')]);
var jsMetadataLib = jsLib
.exportedLibraries
.singleWhere((l) => l.name == 'js.metadata');
var jsMetadataLib = getMetadataLib(jsLib);
var testSourceFile = new SourceFile(testLibSource);
var transaction = new TextEditTransaction(testLibSource, testSourceFile);
var id = new AssetId('test', 'lib/test.dart');
Expand Down
2 changes: 2 additions & 0 deletions test/transformer/library_transformer_test.dart
Expand Up @@ -20,6 +20,8 @@ main() {
var testHelper = new TestHelper([[transformer]], {
'test|lib/library.dart': readTestFile('library.dart'),
'js|lib/js.dart': readJsPackageFile('js.dart'),
'js|lib/src/mirrors.dart': readJsPackageFile('src/mirrors.dart'),
'js|lib/src/static.dart': readJsPackageFile('src/static.dart'),
'js|lib/src/js_impl.dart': readJsPackageFile('src/js_impl.dart'),
'js|lib/src/metadata.dart': readJsPackageFile('src/metadata.dart'),
}, null);
Expand Down
49 changes: 49 additions & 0 deletions test/transformer/remove_mirrors_transformer_test.dart
@@ -0,0 +1,49 @@
// Copyright (c) 2014, 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 js.remove_mirrors_transformer_test;

import 'dart:async';

import 'package:barback/barback.dart';
import 'package:js/src/transformer/remove_mirrors_transformer.dart';
import 'package:unittest/unittest.dart';

import 'utils.dart';

main() {

group("$RemoveMirrorsTransformer", () {
test('should replace the mirrors.dart import', () {
var transformer = new RemoveMirrorsTransformer();
var barback = new Barback(new TestPackageProvider());
barback.updateTransformers('js', [[transformer]]);
var id = new AssetId('js', 'lib/js.dart');
barback.updateSources([id]);
return barback.getAssetById(id)
.then((asset) => asset.readAsString())
.then((source) {
expect(source,
contains("export 'package:js/src/static.dart';"));
expect(source,
isNot(contains(
"export 'package:js/src/mirrors.dart';")));
});
});
});

}

class TestPackageProvider implements PackageProvider {
Iterable<String> get packages => ['js'];

Future<Asset> getAsset(AssetId id) {
if (id.package == 'js' && id.path == 'lib/js.dart') {
return new Future.value(
new Asset.fromString(id, readJsPackageFile('js.dart')));
}
return null;
}

}
6 changes: 2 additions & 4 deletions test/transformer/scanning_visitor_test.dart
Expand Up @@ -6,6 +6,7 @@ library js.test.transformer.scanning_visitor_test;

import 'package:analyzer/src/generated/element.dart';
import 'package:js/src/transformer/scanning_visitor.dart';
import 'package:js/src/transformer/utils.dart';
import 'package:js/src/js_elements.dart';
import 'package:unittest/unittest.dart';

Expand All @@ -22,10 +23,7 @@ main() {
var analyserInfo = initAnalyzer();
testLib = analyserInfo.testLib;
jsLib = analyserInfo.jsLib;
jsMetadataLib = jsLib
.exportedLibraries
.singleWhere((l) => l.name == 'js.metadata');

jsMetadataLib = getMetadataLib(jsLib);
});

test('should find JsProxy annotated classes', () {
Expand Down
1 change: 0 additions & 1 deletion test/transformer/test_sources/library.dart
Expand Up @@ -4,7 +4,6 @@

library test.library;

import 'dart:js' show JsObject;
import 'package:js/js.dart';

abstract class Context extends JsInterface {
Expand Down

0 comments on commit cb2da41

Please sign in to comment.