Chromium Code Reviews| Index: dart/sdk/lib/_internal/compiler/implementation/js_emitter/reflection_data_parser.dart |
| diff --git a/dart/sdk/lib/_internal/compiler/implementation/js_emitter/reflection_data_parser.dart b/dart/sdk/lib/_internal/compiler/implementation/js_emitter/reflection_data_parser.dart |
| index 864f130d9538c355200697d8855360af580ec988..2e218f5accfd48bb3aba605b632e8c5631f49e55 100644 |
| --- a/dart/sdk/lib/_internal/compiler/implementation/js_emitter/reflection_data_parser.dart |
| +++ b/dart/sdk/lib/_internal/compiler/implementation/js_emitter/reflection_data_parser.dart |
| @@ -4,21 +4,183 @@ |
| part of dart2js.js_emitter; |
| +// TODO(ahe): Share these with js_helper.dart. |
| +const FUNCTION_INDEX = 0; |
| +const NAME_INDEX = 1; |
| +const CALL_NAME_INDEX = 2; |
| +const REQUIRED_PARAMETER_INDEX = 3; |
| +const OPTIONAL_PARAMETER_INDEX = 4; |
| +const DEFAULT_ARGUMENTS_INDEX = 5; |
| + |
| // TODO(ahe): This code should be integrated in CodeEmitterTask.finishClasses. |
| -String getReflectionDataParser(String classesCollector, Namer namer) { |
| +String getReflectionDataParser(String classesCollector, |
| + JavaScriptBackend backend) { |
| + Namer namer = backend.namer; |
| + Compiler compiler = backend.compiler; |
| + Element closureFromTearOff = compiler.findHelper('closureFromTearOff'); |
| + String tearOff = |
| + // Default value for mocked-up test libraries. |
| + 'function () { throw "Helper \'closureFromTearOff\' missing." }'; |
| + if (closureFromTearOff != null) { |
| + tearOff = namer.isolateAccess(closureFromTearOff); |
| + } |
| String metadataField = '"${namer.metadataField}"'; |
| String reflectableField = namer.reflectableField; |
| + String reflectionInfoField = r'$reflectionInfo'; // MOVE TO NAMER |
|
Johnni Winther
2013/12/05 11:49:56
Add TODO for the comments.
ahe
2013/12/06 15:57:54
Done.
|
| + String reflectionNameField = r'$reflectionName'; // DITTO |
| + String metadataIndexField = r'$metadataIndex'; // DITTO |
| String defaultValuesField = namer.defaultValuesField; |
| String methodsWithOptionalArgumentsField = |
| namer.methodsWithOptionalArgumentsField; |
| return ''' |
| (function (reflectionData) { |
| + "use strict"; |
| ''' |
| // [map] returns an object literal that V8 shouldn't try to optimize with a |
| // hidden class. This prevents a potential performance problem where V8 tries |
| // to build a hidden class for an object used as a hashMap. |
| ''' |
| function map(x){x={x:x};delete x.x;return x} |
| + function processStatics(descriptor) { |
| + for (var property in descriptor) { |
| + if (!hasOwnProperty.call(descriptor, property)) continue; |
| + if (property === "") continue; |
| + var element = descriptor[property]; |
| + var firstChar = property.substring(0, 1); |
| + var previousProperty; |
| + if (firstChar === "+") { |
|
Johnni Winther
2013/12/05 11:49:56
It would be nice to have a comment that gives an e
ahe
2013/12/06 15:57:54
https://code.google.com/p/dart/issues/detail?id=15
|
| + mangledGlobalNames[previousProperty] = property.substring(1); |
| + if (descriptor[property] == 1) ''' // Break long line. |
| +'''descriptor[previousProperty].$reflectableField = 1; |
|
Johnni Winther
2013/12/05 11:49:56
Indent so `descriptor[previousProperty]` is below
ahe
2013/12/06 15:57:54
Done.
|
| + if (element && element.length) ''' // Break long line. |
| +'''init.typeInformation[previousProperty] = element; |
|
Johnni Winther
2013/12/05 11:49:56
Ditto.
ahe
2013/12/06 15:57:54
Done.
|
| + } else if (firstChar === "@") { |
| + property = property.substring(1); |
| + ${namer.currentIsolate}[property][$metadataField] = element; |
| + } else if (firstChar === "*") { |
| + globalObject[previousProperty].$defaultValuesField = element; |
| + var optionalMethods = descriptor.$methodsWithOptionalArgumentsField; |
| + if (!optionalMethods) { |
| + descriptor.$methodsWithOptionalArgumentsField = optionalMethods = {} |
| + } |
| + optionalMethods[property] = previousProperty; |
| + } else if (typeof element === "function") { |
| + globalObject[previousProperty = property] = element; |
| + functions.push(property); |
| + init.globalFunctions[property] = element; |
| + } else if (element.constructor === Array) { |
| + addStubs(globalObject, element, property, true, descriptor, functions); |
| + } else { |
| + previousProperty = property; |
| + var newDesc = {}; |
| + var previousProp; |
| + for (var prop in element) { |
| + if (!hasOwnProperty.call(element, prop)) continue; |
| + firstChar = prop.substring(0, 1); |
| + if (prop === "static") { |
| + processStatics(init.statics[property] = element[prop]); |
| + } else if (firstChar === "+") { |
| + mangledNames[previousProp] = prop.substring(1); |
| + if (element[prop] == 1) ''' // Break long line. |
| +'''element[previousProp].$reflectableField = 1; |
|
Johnni Winther
2013/12/05 11:49:56
Ditto.
ahe
2013/12/06 15:57:54
Done.
|
| + } else if (firstChar === "@" && prop !== "@") { |
| + newDesc[prop.substring(1)][$metadataField] = element[prop]; |
| + } else if (firstChar === "*") { |
| + newDesc[previousProp].$defaultValuesField = element[prop]; |
| + var optionalMethods = newDesc.$methodsWithOptionalArgumentsField; |
| + if (!optionalMethods) { |
| + newDesc.$methodsWithOptionalArgumentsField = optionalMethods={} |
| + } |
| + optionalMethods[prop] = previousProp; |
| + } else { |
| + var elem = element[prop]; |
| + if (prop && elem != null && elem.constructor === Array && prop !== "<>") { |
|
Johnni Winther
2013/12/05 11:49:56
Long line.
ahe
2013/12/06 15:57:54
Done.
|
| + addStubs(newDesc, elem, prop, false, element, []); |
| + } else { |
| + newDesc[previousProp = prop] = elem; |
| + } |
| + } |
| + } |
| + $classesCollector[property] = [globalObject, newDesc]; |
| + classes.push(property); |
| + } |
| + } |
| + }${ |
| + /** |
| + * See [dart2js.js_emitter.ContainerBuilder.addMemberMethod] for format of |
| + * [array]. |
| + */ ""} |
| + function addStubs(descriptor, array, name, isStatic, originalDescriptor, functions) { |
|
Johnni Winther
2013/12/05 11:49:56
Long line.
ahe
2013/12/06 15:57:54
Done.
|
| + var f, funcs = [originalDescriptor[name] = descriptor[name] = f = ${readFunction("array", "$FUNCTION_INDEX")}]; |
|
Johnni Winther
2013/12/05 11:49:56
Ditto.
ahe
2013/12/06 15:57:54
Done.
|
| + f.\$stubName = name; |
| + functions.push(name); |
| + for (var index = $FUNCTION_INDEX; index < array.length; index += 2) { |
| + f = array[index + 1]; |
| + if (typeof f != "function") break; |
| + f.\$stubName = ${readString("array", "index + 2")}; |
| + funcs.push(f); |
| + // print("Stub for " + name + ": " + f.\$stubName); |
|
Johnni Winther
2013/12/05 11:49:56
Remove debug code.
ahe
2013/12/06 15:57:54
Done.
|
| + if (f.\$stubName) { |
| + originalDescriptor[f.\$stubName] = descriptor[f.\$stubName] = f; |
| + functions.push(f.\$stubName); |
| + } |
| + } |
| + for (var i = 0; i < funcs.length; index++, i++) { |
| + funcs[i].\$callName = ${readString("array", "index + 1")}; |
| + } |
| + var getterStubName = ${readString("array", "++index")}; |
| + array = array.slice(++index); |
| + var requiredParameterInfo = ${readInt("array", "0")}; |
| + var requiredParameterCount = requiredParameterInfo >> 1; |
| + var isAccessor = (requiredParameterInfo & 1) === 1; |
| + var isSetter = requiredParameterInfo === 3; |
| + var isGetter = requiredParameterInfo === 1; |
| + var optionalParameterInfo = ${readInt("array", "1")}; |
| + var optionalParameterCount = optionalParameterInfo >> 1; |
| + var optionalParametersAreNamed = (optionalParameterInfo & 1) === 1; |
| + var functionTypeIndex = ${readFunctionType("array", "2")}; |
| + var isReflectable = array.length > optionalParameterCount + 3; |
| + if (getterStubName) { |
| + f = tearOff(funcs, array, isStatic, name);${ |
|
Johnni Winther
2013/12/05 11:49:56
Replace ${ /* ... */ ""} by ''' // ... '''.
ahe
2013/12/06 15:57:54
OK, but I don't think the code is more readable (o
|
| + /* Used to create an isolate using spawnFunction.*/ ""} |
| + if (isStatic) init.globalFunctions[name] = f; |
| + originalDescriptor[getterStubName] = descriptor[getterStubName] = f; |
| + funcs.push(f); |
| + if (getterStubName) functions.push(getterStubName); |
| + f.\$stubName = getterStubName; |
| + f.\$callName = null; |
| + } |
| + if (isReflectable) { |
| + for (var i = 0; i < funcs.length; i++) { |
| + funcs[i].$reflectableField = 1; |
| + funcs[i].$reflectionInfoField = array; |
| + } |
| + } |
| + if (isReflectable) { |
| + var unmangledNameIndex = optionalParameterCount * 2 + requiredParameterCount + 3; |
|
Johnni Winther
2013/12/05 11:49:56
Long line.
ahe
2013/12/06 15:57:54
Done.
|
| + var unmangledName = ${readString("array", "unmangledNameIndex")}; |
| + var reflectionName = unmangledName + ":" + requiredParameterCount + ":" + optionalParameterCount; |
|
Johnni Winther
2013/12/05 11:49:56
Long line.
ahe
2013/12/06 15:57:54
Done.
|
| + if (isGetter) { |
| + reflectionName = unmangledName; |
| + } else if (isSetter) { |
| + reflectionName = unmangledName + "="; |
| + } |
| + if (isStatic) { |
| + init.mangledGlobalNames[name] = reflectionName; |
| + } else { |
| + init.mangledNames[name] = reflectionName; |
| + } |
| + // print(name + " is reflectable with reflection name \'" + reflectionName + "\' " + isStatic); |
|
Johnni Winther
2013/12/05 11:49:56
Long line.
ahe
2013/12/06 15:57:54
Done.
|
| + funcs[0].$reflectionNameField = reflectionName; |
| + funcs[0].$metadataIndexField = unmangledNameIndex + 1; |
| + if (optionalParameterCount) descriptor[unmangledName + "*"] = funcs[0]; |
| + } |
| + } |
| + function tearOff(funcs, reflectionInfo, isStatic, name) { |
| + return function() { |
| + return $tearOff(this, funcs, reflectionInfo, isStatic, arguments, name); |
| + } |
| + } |
| if (!init.libraries) init.libraries = []; |
| if (!init.mangledNames) init.mangledNames = map(); |
| if (!init.mangledGlobalNames) init.mangledGlobalNames = map(); |
| @@ -53,67 +215,45 @@ String getReflectionDataParser(String classesCollector, Namer namer) { |
| var fields = descriptor && descriptor[""]; |
| var classes = []; |
| var functions = []; |
| - function processStatics(descriptor) { |
| - for (var property in descriptor) { |
| - if (!hasOwnProperty.call(descriptor, property)) continue; |
| - if (property === "") continue; |
| - var element = descriptor[property]; |
| - var firstChar = property.substring(0, 1); |
| - var previousProperty; |
| - if (firstChar === "+") { |
| - mangledGlobalNames[previousProperty] = property.substring(1); |
| - if (descriptor[property] == 1) ''' // Break long line. |
| -'''descriptor[previousProperty].$reflectableField = 1; |
| - if (element && element.length) ''' // Break long line. |
| -'''init.typeInformation[previousProperty] = element; |
| - } else if (firstChar === "@") { |
| - property = property.substring(1); |
| - ${namer.currentIsolate}[property][$metadataField] = element; |
| - } else if (firstChar === "*") { |
| - globalObject[previousProperty].$defaultValuesField = element; |
| - var optionalMethods = descriptor.$methodsWithOptionalArgumentsField; |
| - if (!optionalMethods) { |
| - descriptor.$methodsWithOptionalArgumentsField = optionalMethods = {} |
| - } |
| - optionalMethods[property] = previousProperty; |
| - } else if (typeof element === "function") { |
| - globalObject[previousProperty = property] = element; |
| - functions.push(property); |
| - init.globalFunctions[property] = element; |
| - } else { |
| - previousProperty = property; |
| - var newDesc = {}; |
| - var previousProp; |
| - for (var prop in element) { |
| - if (!hasOwnProperty.call(element, prop)) continue; |
| - firstChar = prop.substring(0, 1); |
| - if (prop === "static") { |
| - processStatics(init.statics[property] = element[prop]); |
| - } else if (firstChar === "+") { |
| - mangledNames[previousProp] = prop.substring(1); |
| - if (element[prop] == 1) ''' // Break long line. |
| -'''element[previousProp].$reflectableField = 1; |
| - } else if (firstChar === "@" && prop !== "@") { |
| - newDesc[prop.substring(1)][$metadataField] = element[prop]; |
| - } else if (firstChar === "*") { |
| - newDesc[previousProp].$defaultValuesField = element[prop]; |
| - var optionalMethods = newDesc.$methodsWithOptionalArgumentsField; |
| - if (!optionalMethods) { |
| - newDesc.$methodsWithOptionalArgumentsField = optionalMethods={} |
| - } |
| - optionalMethods[prop] = previousProp; |
| - } else { |
| - newDesc[previousProp = prop] = element[prop]; |
| - } |
| - } |
| - $classesCollector[property] = [globalObject, newDesc]; |
| - classes.push(property); |
| - } |
| - } |
| - } |
| processStatics(descriptor); |
| libraries.push([name, uri, classes, functions, metadata, fields, isRoot, |
| globalObject]); |
| } |
| })'''; |
| } |
| + |
| +String readString(String array, String index) { |
| + return readChecked( |
| + array, index, 'result != null && typeof result != "string"', 'string'); |
| +} |
| + |
| +String readInt(String array, String index) { |
| + return readChecked( |
| + array, index, 'result != null && (typeof result != "number" || (result|0) !== result)', |
|
Johnni Winther
2013/12/05 11:49:56
Long line.
ahe
2013/12/06 15:57:54
Done.
|
| + 'int'); |
| +} |
| + |
| +String readFunction(String array, String index) { |
| + return readChecked( |
| + array, index, 'result != null && typeof result != "function"', |
| + 'function'); |
| +} |
| + |
| +String readFunctionType(String array, String index) { |
| + return readChecked( |
| + array, index, 'result != null && (typeof result != "number" || (result|0) !== result) && typeof result != "function"', |
|
Johnni Winther
2013/12/05 11:49:56
Ditto.
ahe
2013/12/06 15:57:54
Done.
|
| + 'function or int'); |
| +} |
| + |
| +String readChecked(String array, String index, String check, String type) { |
| + return ''' |
| +(function() { |
|
Johnni Winther
2013/12/05 11:49:56
Only return an applied function in checked mode?
ahe
2013/12/06 15:57:54
Done.
|
| + var result = $array[$index]; |
| + if ($check) { |
| + throw new Error( |
| + name + ": expected value of type \'$type\' at index " + ($index) + |
| + " but got " + (typeof result)); |
| + } |
| + return result; |
| +})()'''; |
| +} |