blob: a2f7a6b28afe10e53dfab274b5874db35583cf83 [file] [log] [blame]
// Copyright (c) 2019, 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.
// Generates the API tables used by DartFuzz. Automatically generating these
// tables is less error-prone than generating such tables by hand. Furthermore,
// it simplifies regenerating the table when the libraries change.
//
// Usage:
// dart gen_api_table.dart > dartfuzz_api_table.dart
//
// Then send out modified dartfuzz_api_table.dart for review together
// with a modified dartfuzz.dart that increases the version.
import 'dart:io';
import 'package:analyzer/dart/analysis/results.dart';
import 'package:analyzer/dart/analysis/session.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
import 'gen_util.dart';
// Class to represent a library method by name and prototype representation.
class DartLib {
final String name;
final String proto;
const DartLib(this.name, this.proto);
}
// Lists of recognized methods, organized by return type.
var voidTable = <DartLib>[];
var boolTable = <DartLib>[];
var intTable = <DartLib>[];
var doubleTable = <DartLib>[];
var stringTable = <DartLib>[];
var listTable = <DartLib>[];
var setTable = <DartLib>[];
var mapTable = <DartLib>[];
var int8ListTable = <DartLib>[];
var int16ListTable = <DartLib>[];
var int32ListTable = <DartLib>[];
var int32x4Table = <DartLib>[];
var int32x4ListTable = <DartLib>[];
var int64ListTable = <DartLib>[];
var float32ListTable = <DartLib>[];
var float32x4ListTable = <DartLib>[];
var float32x4Table = <DartLib>[];
var float64ListTable = <DartLib>[];
var float64x2Table = <DartLib>[];
var float64x2ListTable = <DartLib>[];
var uint8ClampedListTable = <DartLib>[];
var uint8ListTable = <DartLib>[];
var uint16ListTable = <DartLib>[];
var uint32ListTable = <DartLib>[];
var uint64ListTable = <DartLib>[];
const voidEncoding = 'V';
const boolEncoding = 'B';
const intEncoding = 'I';
const doubleEncoding = 'D';
const stringEncoding = 'S';
const listEncoding = 'L';
const setEncoding = 'X';
const mapEncoding = 'M';
const int8ListEncoding = 'A';
const int16ListEncoding = 'C';
const int32ListEncoding = 'E';
const int32x4Encoding = 'F';
const int32x4ListEncoding = 'G';
const int64ListEncoding = 'H';
const float32ListEncoding = 'J';
const float32x4Encoding = 'K';
const float32x4ListEncoding = 'N';
const float64ListEncoding = 'O';
const float64x2Encoding = 'P';
const float64x2ListEncoding = 'Q';
const uint8ClampedListEncoding = 'R';
const uint8ListEncoding = 'T';
const uint16ListEncoding = 'U';
const uint32ListEncoding = 'W';
const uint64ListEncoding = 'Y';
final voidLibs = 'voidLibs';
final boolLibs = 'boolLibs';
final intLibs = 'intLibs';
final doubleLibs = 'doubleLibs';
final stringLibs = 'stringLibs';
final listLibs = 'listLibs';
final setLibs = 'setLibs';
final mapLibs = 'mapLibs';
final int8ListLibs = 'int8ListLibs';
final int16ListLibs = 'int16ListLibs';
final int32ListLibs = 'int32ListLibs';
final int32x4Libs = 'int32x4Libs';
final int32x4ListLibs = 'int32x4ListLibs';
final int64ListLibs = 'int64ListLibs';
final float32ListLibs = 'float32ListLibs';
final float32x4Libs = 'float32x4Libs';
final float32x4ListLibs = 'float32x4ListLibs';
final float64ListLibs = 'float64ListLibs';
final float64x2Libs = 'float64x2Libs';
final float64x2ListLibs = 'float64x2ListLibs';
final uint8ClampedListLibs = 'uint8ClampedListLibs';
final uint8ListLibs = 'uint8ListLibs';
final uint16ListLibs = 'uint16ListLibs';
final uint32ListLibs = 'uint32ListLibs';
final uint64ListLibs = 'uint64ListLibs';
final stringToType = {
voidEncoding: 'DartType.VOID',
boolEncoding: 'DartType.BOOL',
intEncoding: 'DartType.INT',
doubleEncoding: 'DartType.DOUBLE',
stringEncoding: 'DartType.STRING',
listEncoding: 'DartType.LIST_INT',
setEncoding: 'DartType.SET_INT',
mapEncoding: 'DartType.MAP_INT_STRING',
int8ListEncoding: 'DartType.INT8LIST',
int16ListEncoding: 'DartType.INT16LIST',
int32ListEncoding: 'DartType.INT32LIST',
int32x4Encoding: 'DartType.INT32X4',
int32x4ListEncoding: 'DartType.INT32X4LIST',
int64ListEncoding: 'DartType.INT64LIST',
float32ListEncoding: 'DartType.FLOAT32LIST',
float32x4Encoding: 'DartType.FLOAT32X4',
float32x4ListEncoding: 'DartType.FLOAT32X4LIST',
float64ListEncoding: 'DartType.FLOAT64LIST',
float64x2Encoding: 'DartType.FLOAT64X2',
float64x2ListEncoding: 'DartType.FLOAT64X2LIST',
uint8ClampedListEncoding: 'DartType.UINT8CLAMPEDLIST',
uint8ListEncoding: 'DartType.UINT8LIST',
uint16ListEncoding: 'DartType.UINT16LIST',
uint32ListEncoding: 'DartType.UINT32LIST',
uint64ListEncoding: 'DartType.UINT64LIST'
};
final typeToLibraryMethods = {
'DartType.VOID': voidLibs,
'DartType.BOOL': boolLibs,
'DartType.INT': intLibs,
'DartType.DOUBLE': doubleLibs,
'DartType.STRING': stringLibs,
'DartType.LIST_INT': listLibs,
'DartType.SET_INT': setLibs,
'DartType.MAP_INT_STRING': mapLibs,
'DartType.INT8LIST': int8ListLibs,
'DartType.INT16LIST': int16ListLibs,
'DartType.INT32LIST': int32ListLibs,
'DartType.INT32X4': int32x4Libs,
'DartType.INT32X4LIST': int32x4ListLibs,
'DartType.INT64LIST': int64ListLibs,
'DartType.FLOAT32LIST': float32ListLibs,
'DartType.FLOAT32X4': float32x4Libs,
'DartType.FLOAT32X4LIST': float32x4ListLibs,
'DartType.FLOAT64LIST': float64ListLibs,
'DartType.FLOAT64X2': float64x2Libs,
'DartType.FLOAT64X2LIST': float64x2ListLibs,
'DartType.UINT8CLAMPEDLIST': uint8ClampedListLibs,
'DartType.UINT8LIST': uint8ListLibs,
'DartType.UINT16LIST': uint16ListLibs,
'DartType.UINT32LIST': uint32ListLibs,
'DartType.UINT64LIST': uint64ListLibs
};
final typedDataFloatTypes = [
float32ListEncoding,
float32x4Encoding,
float32x4ListEncoding,
float64ListEncoding,
float64x2Encoding,
float64x2ListEncoding
];
main() async {
final AnalysisSession session = GenUtil.createAnalysisSession();
// Visit libraries for table generation.
await visitLibraryAtUri(session, 'dart:async');
await visitLibraryAtUri(session, 'dart:cli');
await visitLibraryAtUri(session, 'dart:collection');
await visitLibraryAtUri(session, 'dart:convert');
await visitLibraryAtUri(session, 'dart:core');
await visitLibraryAtUri(session, 'dart:io');
await visitLibraryAtUri(session, 'dart:isolate');
await visitLibraryAtUri(session, 'dart:math');
await visitLibraryAtUri(session, 'dart:typed_data');
// Generate the tables in a stand-alone Dart class.
dumpHeader();
dumpStringToTypeMap();
dumpTypeToLibraryMethodMap();
dumpTypedDataFloatTypes();
dumpTable(voidLibs, voidTable);
dumpTable(boolLibs, boolTable);
dumpTable(intLibs, intTable);
dumpTable(doubleLibs, doubleTable);
dumpTable(stringLibs, stringTable);
dumpTable(listLibs, listTable);
dumpTable(setLibs, setTable);
dumpTable(mapLibs, mapTable);
dumpTable(int8ListLibs, int8ListTable);
dumpTable(int16ListLibs, int16ListTable);
dumpTable(int32ListLibs, int32ListTable);
dumpTable(int32x4Libs, int32x4Table);
dumpTable(int32x4ListLibs, int32x4ListTable);
dumpTable(int64ListLibs, int64ListTable);
dumpTable(float32ListLibs, float32ListTable);
dumpTable(float32x4Libs, float32x4Table);
dumpTable(float32x4ListLibs, float32x4ListTable);
dumpTable(float64ListLibs, float64ListTable);
dumpTable(float64x2Libs, float64x2Table);
dumpTable(float64x2ListLibs, float64x2ListTable);
dumpTable(uint8ClampedListLibs, uint8ClampedListTable);
dumpTable(uint8ListLibs, uint8ListTable);
dumpTable(uint16ListLibs, uint16ListTable);
dumpTable(uint32ListLibs, uint32ListTable);
dumpTable(uint64ListLibs, uint64ListTable);
dumpFooter();
}
visitLibraryAtUri(AnalysisSession session, String uri) async {
final String libPath = session.uriConverter.uriToPath(Uri.parse(uri));
ResolvedLibraryResult result = await session.getResolvedLibrary(libPath);
if (result.state != ResultState.VALID) {
throw StateError('Unable to resolve "$uri"');
}
visitLibrary(result.element);
}
visitLibrary(LibraryElement library) async {
// This uses the element model to traverse the code. The element model
// represents the semantic structure of the code. A library consists of
// one or more compilation units.
for (CompilationUnitElement unit in library.units) {
visitCompilationUnit(unit);
}
}
visitCompilationUnit(CompilationUnitElement unit) {
// Each compilation unit contains elements for all of the top-level
// declarations in a single file, such as variables, functions, and
// classes. Note that `types` only returns classes. You can use
// `mixins` to visit mixins, `enums` to visit enum, `functionTypeAliases`
// to visit typedefs, etc.
for (TopLevelVariableElement variable in unit.topLevelVariables) {
if (variable.isPublic) {
addToTable(typeString(variable.type), variable.name, 'Vv');
}
}
for (FunctionElement function in unit.functions) {
if (function.isPublic && !function.isOperator) {
addToTable(typeString(function.returnType), function.name,
protoString(null, function.parameters));
}
}
for (ClassElement classElement in unit.types) {
if (classElement.isPublic) {
visitClass(classElement);
}
}
}
void visitClass(ClassElement classElement) {
// Classes that cause too many false divergences.
if (classElement.name == 'ProcessInfo' ||
classElement.name == 'Platform' ||
classElement.name.startsWith('FileSystem')) {
return;
}
// Every class element contains elements for the members, viz. `methods` visits
// methods, `fields` visits fields, `accessors` visits getters and setters, etc.
// There are also accessors to get the superclass, mixins, interfaces, type
// parameters, etc.
for (ConstructorElement constructor in classElement.constructors) {
if (constructor.isPublic &&
constructor.isFactory &&
constructor.name.isNotEmpty) {
addToTable(
typeString(classElement.thisType),
'${classString(classElement)}.${constructor.name}',
protoString(null, constructor.parameters));
}
}
for (MethodElement method in classElement.methods) {
if (method.isPublic && !method.isOperator) {
if (method.isStatic) {
addToTable(
typeString(method.returnType),
'${classString(classElement)}.${method.name}',
protoString(null, method.parameters));
} else {
addToTable(typeString(method.returnType), method.name,
protoString(classElement.thisType, method.parameters));
}
}
}
for (PropertyAccessorElement accessor in classElement.accessors) {
if (accessor.isPublic && accessor.isGetter) {
var variable = accessor.variable;
if (accessor.isStatic) {
addToTable(typeString(variable.type),
'${classElement.name}.${variable.name}', 'Vv');
} else {
addToTable(typeString(variable.type), variable.name,
'${typeString(classElement.thisType)}v');
}
}
}
}
// Function that returns the explicit class name.
String classString(ClassElement classElement) {
switch (typeString(classElement.thisType)) {
case setEncoding:
return 'Set<int>';
case listEncoding:
return 'List<int>';
case mapEncoding:
return 'Map<int, String>';
default:
return classElement.name;
}
}
// Types are represented by an instance of `DartType`. For classes, the type
// will be an instance of `InterfaceType`, which will provide access to the
// defining (class) element, as well as any type arguments.
String typeString(DartType type) {
if (type.isDartCoreBool) {
return boolEncoding;
} else if (type.isDartCoreInt) {
return intEncoding;
} else if (type.isDartCoreDouble) {
return doubleEncoding;
} else if (type.isDartCoreString) {
return stringEncoding;
}
// Supertypes or type parameters are specialized in a consistent manner.
// TODO(ajcbik): inspect type structure semantically, not by display name
// and unify DartFuzz's DartType with analyzer DartType.
switch (type.displayName) {
case 'void':
return voidEncoding;
case 'E':
return intEncoding;
case 'num':
return doubleEncoding;
case 'List<E>':
case 'List<Object>':
case 'List<dynamic>':
case 'List<int>':
case 'List':
return listEncoding;
case 'Set<E>':
case 'Set<Object>':
case 'Set<dynamic>':
case 'Set<int>':
case 'Set':
return setEncoding;
case 'Map<K, V>':
case 'Map<dynamic, dynamic>':
case 'Map<int, String>':
case 'Map':
return mapEncoding;
// TypedData types.
case 'Int8List':
return int8ListEncoding;
case 'Int16List':
return int16ListEncoding;
case 'Int32List':
return int32ListEncoding;
case 'Int32x4':
return int32x4Encoding;
case 'Int32x4List':
return int32x4ListEncoding;
case 'Int64List':
return int64ListEncoding;
case 'Float32List':
return float32ListEncoding;
case 'Float32x4':
return float32x4Encoding;
case 'Float32x4List':
return float32x4ListEncoding;
case 'Float64List':
return float64ListEncoding;
case 'Float64x2':
return float64x2Encoding;
case 'Float64x2List':
return float64x2ListEncoding;
case 'Uint8ClampedList':
return uint8ClampedListEncoding;
case 'Uint8List':
return uint8ListEncoding;
case 'Uint16List':
return uint16ListEncoding;
case 'Uint32List':
return uint32ListEncoding;
case 'Uint64List':
return uint64ListEncoding;
}
return '?';
}
String protoString(DartType receiver, List<ParameterElement> parameters) {
var proto = receiver == null ? voidEncoding : typeString(receiver);
// Construct prototype for non-named parameters.
for (ParameterElement parameter in parameters) {
if (!parameter.isNamed) {
proto += typeString(parameter.type);
}
}
// Use 'void' for an empty parameter list.
return proto.length == 1 ? proto + voidEncoding : proto;
}
List<DartLib> getTable(String ret) {
switch (ret) {
case voidEncoding:
return voidTable;
case boolEncoding:
return boolTable;
case intEncoding:
return intTable;
case doubleEncoding:
return doubleTable;
case stringEncoding:
return stringTable;
case listEncoding:
return listTable;
case setEncoding:
return setTable;
case mapEncoding:
return mapTable;
// TypedData types.
case int8ListEncoding:
return int8ListTable;
case int16ListEncoding:
return int16ListTable;
case int32ListEncoding:
return int32ListTable;
case int32x4Encoding:
return int32x4Table;
case int32x4ListEncoding:
return int32x4ListTable;
case int64ListEncoding:
return int64ListTable;
case float32ListEncoding:
return float32ListTable;
case float32x4Encoding:
return float32x4Table;
case float32x4ListEncoding:
return float32x4ListTable;
case float64ListEncoding:
return float64ListTable;
case float64x2Encoding:
return float64x2Table;
case float64x2ListEncoding:
return float64x2ListTable;
case uint8ClampedListEncoding:
return uint8ClampedListTable;
case uint8ListEncoding:
return uint8ListTable;
case uint16ListEncoding:
return uint16ListTable;
case uint32ListEncoding:
return uint32ListTable;
case uint64ListEncoding:
return uint64ListTable;
default:
throw ArgumentError('Invalid ret value: $ret');
}
}
void addToTable(String ret, String name, String proto) {
// If any of the type representations contains a question
// mark, this means that DartFuzz' type system cannot
// deal with such an expression yet. So drop the entry.
if (ret.contains('?') || proto.contains('?')) {
return;
}
// Avoid the exit function and other functions that give false divergences.
if (name == 'exit' ||
name == 'pid' ||
name == 'hashCode' ||
name == 'exitCode') {
return;
}
// Restrict parameters for a few hardcoded cases,
// for example, to avoid excessive runtime or memory
// allocation in the generated fuzzing program.
if (name == 'padLeft' || name == 'padRight') {
proto = proto.replaceFirst('IS', 'is');
} else if (name == 'List<int>.filled') {
proto = proto.replaceFirst(intEncoding, 'i');
}
// Add to table.
getTable(ret).add(DartLib(name, proto));
}
void dumpHeader() {
print("""
// Copyright (c) 2019, 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.
import \"dartfuzz_type_table.dart\";
/// Class that represents Dart library methods.
///
/// The invididual lists are organized by return type.
/// The proto string has the following format:
/// +-------> receiver type (V denotes none)
/// |+------> param1 type (V denotes none, v denotes getter)
/// ||+-----> param2 type
/// |||+----> ..
/// ||||
/// 'TTTT..'
/// where:
/// V void
/// v void (special)
/// B bool
/// I int
/// i int (small)
/// D double
/// S String
/// s String (small)
/// L List<int>
/// X Set<int>
/// M Map<int, String>
/// A Int8List
/// C Int16List
/// E Int32List
/// F Int32x4
/// G Int32x4List
/// H Int64List
/// J Float32List
/// K Float32x4
/// N Float32x4List
/// O Float64List
/// P Float64x2
/// Q Float64x2List
/// R uint8ClampedList
/// T uint8List
/// U uint16List
/// W uint32List
/// Y uint64List
/// NOTE: this code has been generated automatically.
///
class DartLib {
final String name;
final String proto;
const DartLib(this.name, this.proto);
""");
}
void dumpStringToTypeMap() {
print(' static const stringToType = {');
for (var key in stringToType.keys) {
print(' \'${key}\': ${stringToType[key]},');
}
print(' };');
}
void dumpTypeToLibraryMethodMap() {
print(' static final typeToLibraryMethods = {');
for (var key in typeToLibraryMethods.keys) {
print(' ${key}: ${typeToLibraryMethods[key]},');
}
print(' };');
}
void dumpTypedDataFloatTypes() {
print(' static const typedDataFloatTypes = [');
for (var type in typedDataFloatTypes) {
print(' \'${type}\',');
}
print(' ];');
}
void dumpTable(String identifier, List<DartLib> table) {
print(' static const $identifier = [');
table.sort((a, b) => a.name.compareTo(b.name));
table.forEach((t) => print(' DartLib(\'${t.name}\', \'${t.proto}\'),'));
print(' ];');
}
void dumpFooter() {
print('}');
}