diff --git a/sdk/lib/_internal/compiler/implementation/enqueue.dart b/sdk/lib/_internal/compiler/implementation/enqueue.dart index eff52a963ce..d9733b6d7ec 100644 --- a/sdk/lib/_internal/compiler/implementation/enqueue.dart +++ b/sdk/lib/_internal/compiler/implementation/enqueue.dart @@ -10,6 +10,9 @@ class EnqueueTask extends CompilerTask { final ResolutionEnqueuer resolution; final CodegenEnqueuer codegen; + } else if (element.isTypedef) { + TypedefElement typedef = element; + typedef.ensureResolved(compiler); String get name => 'Enqueue'; EnqueueTask(Compiler compiler) diff --git a/sdk/lib/_internal/compiler/implementation/js_backend/backend.dart b/sdk/lib/_internal/compiler/implementation/js_backend/backend.dart index e9797e73c17..598031c3baa 100644 --- a/sdk/lib/_internal/compiler/implementation/js_backend/backend.dart +++ b/sdk/lib/_internal/compiler/implementation/js_backend/backend.dart @@ -326,9 +326,6 @@ class JavaScriptBackend extends Backend { /// List of elements that the backend may use. final Set helpersUsed = new Set(); - /// Set of typedefs that are used as type literals. - final Set typedefTypeLiterals = new Set(); - /// All the checked mode helpers. static const checkedModeHelpers = CheckedModeHelper.helpers; @@ -2137,7 +2134,7 @@ class JavaScriptionResolutionCallbacks extends ResolutionCallbacks { // when reflection is used. However, as long as we disable tree-shaking // eagerly it doesn't matter. if (type.isTypedef) { - backend.typedefTypeLiterals.add(type.element); + backend.compiler.world.allTypedefs.add(type.element); } backend.customElementsAnalysis.registerTypeLiteral(type, registry); } diff --git a/sdk/lib/_internal/compiler/implementation/js_backend/native_emitter.dart b/sdk/lib/_internal/compiler/implementation/js_backend/native_emitter.dart index 48bc14705d6..46c4fa1469c 100644 --- a/sdk/lib/_internal/compiler/implementation/js_backend/native_emitter.dart +++ b/sdk/lib/_internal/compiler/implementation/js_backend/native_emitter.dart @@ -244,7 +244,7 @@ class NativeEmitter { emitter.classEmitter.emitClassBuilderWithReflectionData( backend.namer.getNameOfClass(classElement), classElement, builders[classElement], - emitter.getElementDecriptor(classElement)); + emitter.getElementDescriptor(classElement)); emitter.needsDefineClass = true; } } diff --git a/sdk/lib/_internal/compiler/implementation/js_emitter/code_emitter_task.dart b/sdk/lib/_internal/compiler/implementation/js_emitter/code_emitter_task.dart index 7113389e597..fce4cc866db 100644 --- a/sdk/lib/_internal/compiler/implementation/js_emitter/code_emitter_task.dart +++ b/sdk/lib/_internal/compiler/implementation/js_emitter/code_emitter_task.dart @@ -55,6 +55,8 @@ class CodeEmitterTask extends CompilerTask { // TODO(ngeoffray): remove this field. Set instantiatedClasses; + List typedefsNeededForReflection; + JavaScriptBackend get backend => compiler.backend; TypeVariableHandler get typeVariableHandler => backend.typeVariableHandler; @@ -63,9 +65,12 @@ class CodeEmitterTask extends CompilerTask { String get n => compiler.enableMinification ? "" : "\n"; String get N => compiler.enableMinification ? "\n" : ";\n"; + CodeBuffer getBuffer(OutputUnit outputUnit) { + return outputBuffers.putIfAbsent(outputUnit, () => new CodeBuffer()); + } + CodeBuffer get mainBuffer { - return outputBuffers.putIfAbsent(compiler.deferredLoadTask.mainOutputUnit, - () => new CodeBuffer()); + return getBuffer(compiler.deferredLoadTask.mainOutputUnit); } /** @@ -750,6 +755,8 @@ class CodeEmitterTask extends CompilerTask { ClassElement cls = element; if (cls.isUnnamedMixinApplication) return null; return cls.name; + } else if (element.isTypedef) { + return element.name; } throw compiler.internalError(element, 'Do not know how to reflect on this $element.'); @@ -859,7 +866,7 @@ class CodeEmitterTask extends CompilerTask { for (Element element in Elements.sortedByPosition(elements)) { ClassBuilder builder = new ClassBuilder(namer); containerBuilder.addMember(element, builder); - getElementDecriptor(element).properties.addAll(builder.properties); + getElementDescriptor(element).properties.addAll(builder.properties); } } @@ -1145,10 +1152,15 @@ class CodeEmitterTask extends CompilerTask { } } - /** - * Compute all the classes that must be emitted. - */ - void computeNeededClasses() { + /// Compute all the classes and typedefs that must be emitted. + void computeNeededDeclarations() { + // Compute needed typedefs. + typedefsNeededForReflection = Elements.sortedByPosition( + compiler.world.allTypedefs + .where(backend.isNeededForReflection) + .toList()); + + // Compute needed classes. instantiatedClasses = compiler.codegenWorld.instantiatedClasses.where(computeClassFilter()) .toSet(); @@ -1303,21 +1315,17 @@ class CodeEmitterTask extends CompilerTask { if (uri.scheme == 'file' && compiler.outputUri != null) { uri = relativize(compiler.outputUri, library.canonicalUri, false); } - Map descriptors = - elementDescriptors[library]; + Map descriptors = elementDescriptors[library]; for (OutputUnit outputUnit in compiler.deferredLoadTask.allOutputUnits) { - ClassBuilder descriptor = - descriptors.putIfAbsent(outputUnit, () => new ClassBuilder(namer)); - if (descriptor.properties.isEmpty) continue; - bool isDeferred = - outputUnit != compiler.deferredLoadTask.mainOutputUnit; + if (!descriptors.containsKey(outputUnit)) continue; + + ClassBuilder descriptor = descriptors[outputUnit]; jsAst.Fun metadata = metadataEmitter.buildMetadataFunction(library); - jsAst.ObjectInitializer initializers = - descriptor.toObjectInitializer(); - CodeBuffer outputBuffer = - outputBuffers.putIfAbsent(outputUnit, () => new CodeBuffer()); + jsAst.ObjectInitializer initializers = descriptor.toObjectInitializer(); + CodeBuffer outputBuffer = getBuffer(outputUnit); + int sizeBefore = outputBuffer.length; outputBuffers[outputUnit] ..write('["${library.getLibraryName()}",$_') @@ -1343,7 +1351,7 @@ class CodeEmitterTask extends CompilerTask { // 'is$' method. typeTestEmitter.computeRequiredTypeChecks(); - computeNeededClasses(); + computeNeededDeclarations(); mainBuffer.add(buildGeneratedBy()); addComment(HOOKS_API_USAGE, mainBuffer); @@ -1402,7 +1410,7 @@ class CodeEmitterTask extends CompilerTask { // Might create methodClosures. for (List outputClassList in outputClassLists.values) { for (ClassElement element in outputClassList) { - generateClass(element, getElementDecriptor(element)); + generateClass(element, getElementDescriptor(element)); } } @@ -1422,6 +1430,8 @@ class CodeEmitterTask extends CompilerTask { mainBuffer.write(';'); } + // TODO(karlklose): document what kinds of fields this loop adds to the + // library class builder. for (Element element in elementDescriptors.keys) { // TODO(ahe): Should iterate over all libraries. Otherwise, we will // not see libraries that only have fields. @@ -1437,6 +1447,28 @@ class CodeEmitterTask extends CompilerTask { } } + // Emit all required typedef declarations into the main output unit. + // TODO(karlklose): unify required classes and typedefs to declarations + // and have builders for each kind. + for (TypedefElement typedef in typedefsNeededForReflection) { + OutputUnit mainUnit = compiler.deferredLoadTask.mainOutputUnit; + LibraryElement library = typedef.library; + // TODO(karlklose): add a TypedefBuilder and move this code there. + DartType type = typedef.alias; + int typeIndex = metadataEmitter.reifyType(type); + String typeReference = + encoding.encodeTypedefFieldDescriptor(typeIndex); + jsAst.Property descriptor = new jsAst.Property( + js.string(namer.classDescriptorProperty), + js.string(typeReference)); + jsAst.Node declaration = new jsAst.ObjectInitializer([descriptor]); + String mangledName = namer.getNameX(typedef); + String reflectionName = getReflectionName(typedef, mangledName); + getElementDescriptorForOutputUnit(library, mainUnit) + ..addProperty(mangledName, declaration) + ..addProperty("+$reflectionName", js.string('')); + } + if (!mangledFieldNames.isEmpty) { var keys = mangledFieldNames.keys.toList(); keys.sort(); @@ -1689,7 +1721,7 @@ class CodeEmitterTask extends CompilerTask { () => new ClassBuilder(namer)); } - ClassBuilder getElementDecriptor(Element element) { + ClassBuilder getElementDescriptor(Element element) { Element owner = element.library; if (!element.isTopLevel && !element.isNative) { // For static (not top level) elements, record their code in a buffer diff --git a/sdk/lib/_internal/compiler/implementation/js_emitter/js_emitter.dart b/sdk/lib/_internal/compiler/implementation/js_emitter/js_emitter.dart index 129457449d3..93f46d98655 100644 --- a/sdk/lib/_internal/compiler/implementation/js_emitter/js_emitter.dart +++ b/sdk/lib/_internal/compiler/implementation/js_emitter/js_emitter.dart @@ -65,6 +65,8 @@ import '../util/util.dart' show import '../deferred_load.dart' show OutputUnit; +import '../runtime_data.dart' as encoding; + part 'class_builder.dart'; part 'class_emitter.dart'; part 'code_emitter_helper.dart'; diff --git a/sdk/lib/_internal/compiler/implementation/js_emitter/metadata_emitter.dart b/sdk/lib/_internal/compiler/implementation/js_emitter/metadata_emitter.dart index 89fdfa6622a..92b5f552bf7 100644 --- a/sdk/lib/_internal/compiler/implementation/js_emitter/metadata_emitter.dart +++ b/sdk/lib/_internal/compiler/implementation/js_emitter/metadata_emitter.dart @@ -90,20 +90,8 @@ class MetadataEmitter extends CodeEmitterHelper { } void emitMetadata(CodeBuffer buffer) { - var literals = backend.typedefTypeLiterals.toList(); - Elements.sortedByPosition(literals); - var properties = []; - for (TypedefElement literal in literals) { - var key = namer.getNameX(literal); - var value = js.number(reifyType(literal.rawType)); - properties.add(new jsAst.Property(js.string(key), value)); - } - var map = new jsAst.ObjectInitializer(properties); - buffer.write( - jsAst.prettyPrint( - js.statement('init.functionAliases = #', map), compiler)); - buffer.write('${N}init.metadata$_=$_['); - for (var metadata in globalMetadata) { + buffer.write('init.metadata$_=$_['); + for (String metadata in globalMetadata) { if (metadata is String) { if (metadata != 'null') { buffer.write(metadata); diff --git a/sdk/lib/_internal/compiler/implementation/resolution/members.dart b/sdk/lib/_internal/compiler/implementation/resolution/members.dart index b60dfc7b65f..212412890c6 100644 --- a/sdk/lib/_internal/compiler/implementation/resolution/members.dart +++ b/sdk/lib/_internal/compiler/implementation/resolution/members.dart @@ -1189,6 +1189,7 @@ class ResolverTask extends CompilerTask { TreeElements resolveTypedef(TypedefElementX element) { if (element.isResolved) return element.treeElements; + compiler.world.allTypedefs.add(element); return _resolveTypeDeclaration(element, () { ResolutionRegistry registry = new ResolutionRegistry(compiler, element); return compiler.withCurrentElement(element, () { diff --git a/sdk/lib/_internal/compiler/implementation/runtime_data.dart b/sdk/lib/_internal/compiler/implementation/runtime_data.dart new file mode 100644 index 00000000000..0a5688dce52 --- /dev/null +++ b/sdk/lib/_internal/compiler/implementation/runtime_data.dart @@ -0,0 +1,18 @@ +/// Contains encoding, decoding and detection functionality for the +/// representation of program data at runtime. +/// +/// This library is shared between the compiler and the runtime system. +library dart2js.runtime_data; + + +String encodeTypedefFieldDescriptor(int typeIndex) { + return ":$typeIndex;"; +} + +bool isTypedefDescriptor(String descriptor) { + return descriptor.startsWith(':'); +} + +int getTypeFromTypedef(String descriptor) { + return int.parse(descriptor.substring(1, descriptor.length - 1)); +} \ No newline at end of file diff --git a/sdk/lib/_internal/compiler/implementation/world.dart b/sdk/lib/_internal/compiler/implementation/world.dart index b2e9c9c2ed3..9ecb9ea5f15 100644 --- a/sdk/lib/_internal/compiler/implementation/world.dart +++ b/sdk/lib/_internal/compiler/implementation/world.dart @@ -10,6 +10,8 @@ class World { final Set functionsCalledInLoop = new Set(); final Map sideEffects = new Map(); + final Set allTypedefs = new Set(); + final Map> mixinUses = new Map>(); diff --git a/sdk/lib/_internal/lib/js_mirrors.dart b/sdk/lib/_internal/lib/js_mirrors.dart index 4cbdaf93281..cbad64bc742 100644 --- a/sdk/lib/_internal/lib/js_mirrors.dart +++ b/sdk/lib/_internal/lib/js_mirrors.dart @@ -4,7 +4,7 @@ library dart._js_mirrors; -import 'dart:async'; +import '../compiler/implementation/runtime_data.dart' as encoding; import 'dart:collection' show UnmodifiableListView, @@ -319,10 +319,12 @@ class JsLibraryMirror extends JsDeclarationMirror with JsObjectMirror var cls = reflectClassByMangledName(className); if (cls is ClassMirror) { cls = cls.originalDeclaration; - if (cls is JsClassMirror) { - result[cls.simpleName] = cls; - cls._owner = this; - } + } + if (cls is JsClassMirror) { + result[cls.simpleName] = cls; + cls._owner = this; + } else if (cls is JsTypedefMirror) { + result[cls.simpleName] = cls; } } return _cachedClasses = @@ -557,12 +559,6 @@ TypeMirror reflectClassByName(Symbol symbol, String mangledName) { } var constructor = JS('var', 'init.allClasses[#]', mangledName); if (constructor == null) { - int index = JS('int|Null', 'init.functionAliases[#]', mangledName); - if (index != null) { - mirror = new JsTypedefMirror(symbol, mangledName, getMetadata(index)); - JsCache.update(classMirrors, mangledName, mirror); - return mirror; - } // Probably an intercepted class. // TODO(ahe): How to handle intercepted classes? throw new UnsupportedError('Cannot find class for: ${n(symbol)}'); @@ -587,23 +583,28 @@ TypeMirror reflectClassByName(Symbol symbol, String mangledName) { } } - var superclassName = fields.split(';')[0]; - var mixins = superclassName.split('+'); - if (mixins.length > 1 && mangledGlobalNames[mangledName] == null) { - mirror = reflectMixinApplication(mixins, mangledName); + if (encoding.isTypedefDescriptor(fields)) { + int index = encoding.getTypeFromTypedef(fields); + mirror = new JsTypedefMirror(symbol, mangledName, getMetadata(index)); } else { - ClassMirror classMirror = new JsClassMirror( - symbol, mangledName, constructor, fields, fieldsMetadata); - List typeVariables = - JS('JSExtendableArray|Null', '#.prototype["<>"]', constructor); - if (typeVariables == null || typeVariables.length == 0) { - mirror = classMirror; + var superclassName = fields.split(';')[0]; + var mixins = superclassName.split('+'); + if (mixins.length > 1 && mangledGlobalNames[mangledName] == null) { + mirror = reflectMixinApplication(mixins, mangledName); } else { - String typeArguments = 'dynamic'; - for (int i = 1; i < typeVariables.length; i++) { - typeArguments += ',dynamic'; + ClassMirror classMirror = new JsClassMirror( + symbol, mangledName, constructor, fields, fieldsMetadata); + List typeVariables = + JS('JSExtendableArray|Null', '#.prototype["<>"]', constructor); + if (typeVariables == null || typeVariables.length == 0) { + mirror = classMirror; + } else { + String typeArguments = 'dynamic'; + for (int i = 1; i < typeVariables.length; i++) { + typeArguments += ',dynamic'; + } + mirror = new JsTypeBoundClassMirror(classMirror, typeArguments); } - mirror = new JsTypeBoundClassMirror(classMirror, typeArguments); } } diff --git a/tests/compiler/dart2js/analyze_unused_dart2js_test.dart b/tests/compiler/dart2js/analyze_unused_dart2js_test.dart index 03930aaa949..a0cbc0b7c62 100644 --- a/tests/compiler/dart2js/analyze_unused_dart2js_test.dart +++ b/tests/compiler/dart2js/analyze_unused_dart2js_test.dart @@ -22,6 +22,10 @@ const Map> WHITE_LIST = const { // Some things in dart_printer are not yet used "implementation/dart_backend/dart_printer.dart" : const [" is never "], + // dart2js uses only the encoding functions, the decoding functions are used + // from the generated code. + "implementation/runtime_data.dart": const [" is never "], + // Setlet implements the Set interface: Issue 18959. "implementation/util/setlet.dart": const [" is never "], }; diff --git a/tests/lib/lib.status b/tests/lib/lib.status index 16dfa088cf3..9137b8be0bf 100644 --- a/tests/lib/lib.status +++ b/tests/lib/lib.status @@ -201,7 +201,6 @@ async/schedule_microtask6_test: Fail # Issue 10957 - may be related convert/json_lib_test: Fail # Issue 10961 [ $compiler == dart2js && $minified ] -mirrors/typedef_test/01: Fail # http://dartbug.com/6490 mirrors/mirrors_used_get_name_test: RuntimeError mirrors/mirrors_used_get_name2_test: RuntimeError @@ -249,6 +248,8 @@ mirrors/mirrors_used_inheritance_test: RuntimeError # Issue 16048 async/timer_not_available_test: SkipByDesign # only meant to test when there is no way to implement timer (currently only in d8) +mirrors/typedef_declaration_test/01: Fail # dartbug.com/16048. Remove multitest marker when it passes. + [ $compiler == none && ( $runtime == drt || $runtime == dartium || $runtime == ContentShellOnAndroid) ] async/schedule_microtask6_test: Fail # Issue 10910 async/timer_test: Fail, Pass # Issue 15487 diff --git a/tests/lib/mirrors/relation_subclass_test.dart b/tests/lib/mirrors/relation_subclass_test.dart index 2c2c5252142..2e8bbc24793 100644 --- a/tests/lib/mirrors/relation_subclass_test.dart +++ b/tests/lib/mirrors/relation_subclass_test.dart @@ -57,13 +57,12 @@ test(MirrorSystem mirrors) { Expect.isFalse(Obj.isSubclassOf(Func)); // Function typedef. - // TODO(16939): retrieve via declaration when dart2js supports it. - var NumPred = reflectType(NumberPredicate); - var IntPred = reflectType(IntegerPredicate); - var DubPred = reflectType(DoublePredicate); - var NumGen = reflectType(NumberGenerator); - var IntGen = reflectType(IntegerGenerator); - var DubGen = reflectType(DoubleGenerator); + var NumPred = thisLibrary.declarations[#NumberPredicate]; + var IntPred = thisLibrary.declarations[#IntegerPredicate]; + var DubPred = thisLibrary.declarations[#DoublePredicate]; + var NumGen = thisLibrary.declarations[#NumberGenerator]; + var IntGen = thisLibrary.declarations[#IntegerGenerator]; + var DubGen = thisLibrary.declarations[#DoubleGenerator]; isArgumentOrTypeError(e) => e is ArgumentError || e is TypeError; Expect.throws(() => Func.isSubclassOf(NumPred), isArgumentOrTypeError); diff --git a/tests/lib/mirrors/typedef_declaration_test.dart b/tests/lib/mirrors/typedef_declaration_test.dart new file mode 100644 index 00000000000..f039c1b6a2c --- /dev/null +++ b/tests/lib/mirrors/typedef_declaration_test.dart @@ -0,0 +1,29 @@ +// 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 test; + +import 'package:expect/expect.dart'; + +@MirrorsUsed(targets: "Foo") +import 'dart:mirrors'; + +typedef int Foo(String x); +typedef int Bar(); + +main() { + LibraryMirror thisLibrary = currentMirrorSystem().findLibrary(#test); + + Mirror fooMirror = thisLibrary.declarations[#Foo]; + + Expect.isTrue(fooMirror != null, 'Foo not found.'); + Expect.isTrue(thisLibrary.declarations[#Foo] is TypedefMirror, + 'TypedefMirror expected, found $fooMirror'); + + // The following code does not currenty work on the VM, because it does not + // support MirrorsUsed (see dartbug.com/16048). + Mirror barMirror = thisLibrary.declarations[#Bar]; /// 01: ok + Expect.isTrue(barMirror == null, /// 01: continued + 'Bar should not be emitted due to MirrorsUsed.'); /// 01: continued +}