| // Copyright (c) 2013, 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. |
| |
| part of dart2js.js_emitter; |
| |
| class MetadataCollector { |
| final Compiler _compiler; |
| final Emitter _emitter; |
| |
| /// A list of JS expressions that represent metadata, parameter names and |
| /// type variable types. |
| final List<String> globalMetadata = <String>[]; |
| |
| /// A map used to canonicalize the entries of globalMetadata. |
| final Map<String, int> _globalMetadataMap = <String, int>{}; |
| |
| /// A map with lists of JS expressions, one list for each output unit. The |
| /// entries represent types including function types and typedefs. |
| final Map<OutputUnit, List<String>> types = <OutputUnit, List<String>>{}; |
| |
| /// A map used to canonicalize the entries of types. |
| final Map<OutputUnit, Map<String, int>> _typesMap = |
| <OutputUnit, Map<String, int>>{}; |
| |
| MetadataCollector(this._compiler, this._emitter); |
| |
| JavaScriptBackend get _backend => _compiler.backend; |
| TypeVariableHandler get _typeVariableHandler => _backend.typeVariableHandler; |
| |
| bool _mustEmitMetadataFor(Element element) { |
| return _backend.mustRetainMetadata && |
| _backend.referencedFromMirrorSystem(element); |
| } |
| |
| /// The metadata function returns the metadata associated with |
| /// [element] in generated code. The metadata needs to be wrapped |
| /// in a function as it refers to constants that may not have been |
| /// constructed yet. For example, a class is allowed to be |
| /// annotated with itself. The metadata function is used by |
| /// mirrors_patch to implement DeclarationMirror.metadata. |
| jsAst.Fun buildMetadataFunction(Element element) { |
| if (!_mustEmitMetadataFor(element)) return null; |
| return _compiler.withCurrentElement(element, () { |
| List<jsAst.Expression> metadata = <jsAst.Expression>[]; |
| Link link = element.metadata; |
| // TODO(ahe): Why is metadata sometimes null? |
| if (link != null) { |
| for (; !link.isEmpty; link = link.tail) { |
| MetadataAnnotation annotation = link.head; |
| ConstantExpression constant = |
| _backend.constants.getConstantForMetadata(annotation); |
| if (constant == null) { |
| _compiler.internalError(annotation, 'Annotation value is null.'); |
| } else { |
| metadata.add(_emitter.constantReference(constant.value)); |
| } |
| } |
| } |
| if (metadata.isEmpty) return null; |
| return js('function() { return # }', |
| new jsAst.ArrayInitializer(metadata)); |
| }); |
| } |
| |
| List<int> reifyDefaultArguments(FunctionElement function) { |
| FunctionSignature signature = function.functionSignature; |
| if (signature.optionalParameterCount == 0) return const []; |
| List<int> defaultValues = <int>[]; |
| for (ParameterElement element in signature.optionalParameters) { |
| ConstantExpression constant = |
| _backend.constants.getConstantForVariable(element); |
| String stringRepresentation = (constant == null) |
| ? "null" |
| : jsAst.prettyPrint( |
| _emitter.constantReference(constant.value), _compiler).getText(); |
| defaultValues.add(addGlobalMetadata(stringRepresentation)); |
| } |
| return defaultValues; |
| } |
| |
| int reifyMetadata(MetadataAnnotation annotation) { |
| ConstantExpression constant = |
| _backend.constants.getConstantForMetadata(annotation); |
| if (constant == null) { |
| _compiler.internalError(annotation, 'Annotation value is null.'); |
| return -1; |
| } |
| return addGlobalMetadata( |
| jsAst.prettyPrint( |
| _emitter.constantReference(constant.value), _compiler).getText()); |
| } |
| |
| int reifyType(DartType type, {bool ignoreTypeVariables: false}) { |
| return reifyTypeForOutputUnit(type, |
| _compiler.deferredLoadTask.mainOutputUnit, |
| ignoreTypeVariables: ignoreTypeVariables); |
| } |
| |
| int reifyTypeForOutputUnit(DartType type, OutputUnit outputUnit, |
| {bool ignoreTypeVariables: false}) { |
| jsAst.Expression representation = |
| _backend.rti.getTypeRepresentation( |
| type, |
| (variable) { |
| if (ignoreTypeVariables) return new jsAst.LiteralNull(); |
| return js.number( |
| _typeVariableHandler.reifyTypeVariable( |
| variable.element)); |
| }, |
| (TypedefType typedef) { |
| return _backend.isAccessibleByReflection(typedef.element); |
| }); |
| |
| return addTypeInOutputUnit( |
| jsAst.prettyPrint(representation, _compiler).getText(), outputUnit); |
| } |
| |
| int reifyName(String name) { |
| return addGlobalMetadata('"$name"'); |
| } |
| |
| int addGlobalMetadata(String string) { |
| return _globalMetadataMap.putIfAbsent(string, () { |
| globalMetadata.add(string); |
| return globalMetadata.length - 1; |
| }); |
| } |
| |
| int addTypeInOutputUnit(String compiledType, OutputUnit outputUnit) { |
| if (_typesMap[outputUnit] == null) { |
| _typesMap[outputUnit] = <String, int>{}; |
| } |
| return _typesMap[outputUnit].putIfAbsent(compiledType, () { |
| |
| if (types[outputUnit] == null) |
| types[outputUnit] = <String>[]; |
| |
| types[outputUnit].add(compiledType); |
| return types[outputUnit].length - 1; |
| }); |
| } |
| |
| List<int> computeMetadata(FunctionElement element) { |
| return _compiler.withCurrentElement(element, () { |
| if (!_mustEmitMetadataFor(element)) return const <int>[]; |
| List<int> metadata = <int>[]; |
| Link link = element.metadata; |
| // TODO(ahe): Why is metadata sometimes null? |
| if (link != null) { |
| for (; !link.isEmpty; link = link.tail) { |
| metadata.add(reifyMetadata(link.head)); |
| } |
| } |
| return metadata; |
| }); |
| } |
| } |