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 373b65dd369bdeb11dd39c4a24a414d33096fe64..6e1f51a69f258a90de3c8bb21678b4ec195deac9 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 |
+ 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 === "+") { |
+ 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.CURRENT_ISOLATE}[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; |
+ } 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 !== "<>") { |
kasperl
2013/11/29 10:10:55
Long line.
ahe
2013/12/06 15:57:53
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) { |
kasperl
2013/11/29 10:10:55
Long lines.
ahe
2013/12/06 15:57:53
Done.
|
+ var f, funcs = [originalDescriptor[name] = descriptor[name] = f = ${readFunction("array", "$FUNCTION_INDEX")}]; |
+ 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); |
+ 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);${ |
+ /* 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; |
kasperl
2013/11/29 10:10:55
Long lines.
ahe
2013/12/06 15:57:53
Done.
|
+ var unmangledName = ${readString("array", "unmangledNameIndex")}; |
+ var reflectionName = unmangledName + ":" + requiredParameterCount + ":" + optionalParameterCount; |
+ 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); |
+ 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) { |
kasperl
2013/11/29 10:10:55
Maybe it would be easier to review this if you wai
ahe
2013/12/06 15:57:53
Yes. Sorry about that.
|
- 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.CURRENT_ISOLATE}[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)', |
+ '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"', |
+ 'function or int'); |
+} |
+ |
+String readChecked(String array, String index, String check, String type) { |
+ return ''' |
+(function() { |
+ var result = $array[$index]; |
+ if ($check) { |
+ throw new Error( |
+ name + ": expected value of type \'$type\' at index " + ($index) + |
+ " but got " + (typeof result)); |
+ } |
+ return result; |
+})()'''; |
+} |