blob: f4597f3b9946389ee7dedb5a424331c19a09c13a [file] [log] [blame]
// 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 MetadataEmitter extends CodeEmitterHelper {
/// A list of JS expressions that represent metadata, parameter names and
/// type, and return types.
final List<String> globalMetadata = [];
/// A map used to canonicalize the entries of globalMetadata.
final Map<String, int> globalMetadataMap = <String, int>{};
/// 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 (!backend.retainMetadataOf(element)) return null;
return compiler.withCurrentElement(element, () {
var metadata = [];
Link link = element.metadata;
// TODO(ahe): Why is metadata sometimes null?
if (link != null) {
for (; !link.isEmpty; link = link.tail) {
MetadataAnnotation annotation = link.head;
Constant value = annotation.value;
if (value == null) {
compiler.reportInternalError(
annotation, 'Internal error: value is null');
} else {
metadata.add(task.constantReference(value));
}
}
}
if (metadata.isEmpty) return null;
return js.fun(
[], [js.return_(new jsAst.ArrayInitializer.from(metadata))]);
});
}
jsAst.Node reifyDefaultArguments(FunctionElement function) {
FunctionSignature signature = function.computeSignature(compiler);
if (signature.optionalParameterCount == 0) return null;
List<int> defaultValues = <int>[];
for (Element element in signature.orderedOptionalParameters) {
Constant value =
compiler.constantHandler.initialVariableValues[element];
String stringRepresentation = (value == null)
? "null"
: jsAst.prettyPrint(task.constantReference(value), compiler)
.getText();
defaultValues.add(addGlobalMetadata(stringRepresentation));
}
return js.toExpression(defaultValues);
}
int reifyMetadata(MetadataAnnotation annotation) {
Constant value = annotation.value;
if (value == null) {
compiler.reportInternalError(
annotation, 'Internal error: value is null');
return -1;
}
return addGlobalMetadata(
jsAst.prettyPrint(task.constantReference(value), compiler).getText());
}
int reifyType(DartType type) {
// TODO(ahe): Handle type variables correctly instead of using "#".
String representation = backend.rti.getTypeRepresentation(type, (_) {});
return addGlobalMetadata(representation.replaceAll('#', 'null'));
}
int reifyName(SourceString name) {
return addGlobalMetadata('"${name.slowToString()}"');
}
int addGlobalMetadata(String string) {
return globalMetadataMap.putIfAbsent(string, () {
globalMetadata.add(string);
return globalMetadata.length - 1;
});
}
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.toExpression(reifyType(literal.rawType));
properties.add(new jsAst.Property(js.string(key), value));
}
var map = new jsAst.ObjectInitializer(properties);
buffer.write(
jsAst.prettyPrint(
js('init.functionAliases = #', map).toStatement(), compiler));
buffer.write('${N}init.metadata$_=$_[');
for (var metadata in globalMetadata) {
if (metadata is String) {
if (metadata != 'null') {
buffer.write(metadata);
}
} else {
throw 'Unexpected value in metadata: ${Error.safeToString(metadata)}';
}
buffer.write(',$n');
}
buffer.write('];$n');
}
jsAst.Fun extendWithMetadata(FunctionElement element, jsAst.Fun code) {
if (!backend.retainMetadataOf(element)) return code;
return compiler.withCurrentElement(element, () {
List<int> metadata = <int>[];
FunctionSignature signature = element.functionSignature;
if (element.isConstructor()) {
metadata.add(reifyType(element.getEnclosingClass().thisType));
} else {
metadata.add(reifyType(signature.returnType));
}
signature.forEachParameter((Element parameter) {
metadata
..add(reifyName(parameter.name))
..add(reifyType(parameter.computeType(compiler)));
});
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));
}
}
code.body.statements.add(js.string(metadata.join(',')).toStatement());
return code;
});
}
}