blob: 86fdcd8d73a329f65e45f77e81d030ef919cc46c [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;
// 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;
const bool VALIDATE_DATA = false;
const RANGE1_SIZE = RANGE1_LAST - RANGE1_FIRST + 1;
const RANGE2_SIZE = RANGE2_LAST - RANGE2_FIRST + 1;
const RANGE1_ADJUST = - (FIRST_FIELD_CODE - RANGE1_FIRST);
const RANGE2_ADJUST = - (FIRST_FIELD_CODE + RANGE1_SIZE - RANGE2_FIRST);
const RANGE3_ADJUST =
- (FIRST_FIELD_CODE + RANGE1_SIZE + RANGE2_SIZE - RANGE3_FIRST);
String get setupProgramName => 'setupProgram';
jsAst.Statement buildSetupProgram(Program program, Compiler compiler,
JavaScriptBackend backend,
Namer namer,
OldEmitter emitter) {
jsAst.Expression typeInformationAccess =
emitter.generateEmbeddedGlobalAccess(embeddedNames.TYPE_INFORMATION);
jsAst.Expression globalFunctionsAccess =
emitter.generateEmbeddedGlobalAccess(embeddedNames.GLOBAL_FUNCTIONS);
jsAst.Expression staticsAccess =
emitter.generateEmbeddedGlobalAccess(embeddedNames.STATICS);
jsAst.Expression interceptedNamesAccess =
emitter.generateEmbeddedGlobalAccess(embeddedNames.INTERCEPTED_NAMES);
jsAst.Expression mangledGlobalNamesAccess =
emitter.generateEmbeddedGlobalAccess(embeddedNames.MANGLED_GLOBAL_NAMES);
jsAst.Expression mangledNamesAccess =
emitter.generateEmbeddedGlobalAccess(embeddedNames.MANGLED_NAMES);
jsAst.Expression librariesAccess =
emitter.generateEmbeddedGlobalAccess(embeddedNames.LIBRARIES);
jsAst.Expression typesAccess =
emitter.generateEmbeddedGlobalAccess(embeddedNames.TYPES);
jsAst.Expression createNewIsolateFunctionAccess =
emitter.generateEmbeddedGlobalAccess(embeddedNames.CREATE_NEW_ISOLATE);
jsAst.Expression classIdExtractorAccess =
emitter.generateEmbeddedGlobalAccess(embeddedNames.CLASS_ID_EXTRACTOR);
jsAst.Expression allClassesAccess =
emitter.generateEmbeddedGlobalAccess(embeddedNames.ALL_CLASSES);
jsAst.Expression precompiledAccess =
emitter.generateEmbeddedGlobalAccess(embeddedNames.PRECOMPILED);
jsAst.Expression finishedClassesAccess =
emitter.generateEmbeddedGlobalAccess(embeddedNames.FINISHED_CLASSES);
jsAst.Expression interceptorsByTagAccess =
emitter.generateEmbeddedGlobalAccess(embeddedNames.INTERCEPTORS_BY_TAG);
jsAst.Expression leafTagsAccess =
emitter.generateEmbeddedGlobalAccess(embeddedNames.LEAF_TAGS);
jsAst.Expression initializeEmptyInstanceAccess =
emitter.generateEmbeddedGlobalAccess(
embeddedNames.INITIALIZE_EMPTY_INSTANCE);
jsAst.Expression classFieldsExtractorAccess =
emitter.generateEmbeddedGlobalAccess(
embeddedNames.CLASS_FIELDS_EXTRACTOR);
jsAst.Expression instanceFromClassIdAccess =
emitter.generateEmbeddedGlobalAccess(
embeddedNames.INSTANCE_FROM_CLASS_ID);
String reflectableField = namer.reflectableField;
String reflectionInfoField = namer.reflectionInfoField;
String reflectionNameField = namer.reflectionNameField;
String metadataIndexField = namer.metadataIndexField;
String defaultValuesField = namer.defaultValuesField;
String methodsWithOptionalArgumentsField =
namer.methodsWithOptionalArgumentsField;
String unmangledNameIndex = backend.mustRetainMetadata
? ' 3 * optionalParameterCount + 2 * requiredParameterCount + 3'
: ' 2 * optionalParameterCount + requiredParameterCount + 3';
String receiverParamName = compiler.enableMinification ? "r" : "receiver";
String valueParamName = compiler.enableMinification ? "v" : "value";
String space = compiler.enableMinification ? "" : " ";
String _ = space;
String specProperty = '"${namer.nativeSpecProperty}"'; // "%"
jsAst.Expression nativeInfoAccess = js('prototype[$specProperty]', []);
jsAst.Expression constructorAccess = js('constructor', []);
Function subclassReadGenerator =
(jsAst.Expression subclass) => js('allClasses[#]', subclass);
jsAst.Statement nativeInfoHandler = emitter.
buildNativeInfoHandler(nativeInfoAccess, constructorAccess,
subclassReadGenerator, interceptorsByTagAccess,
leafTagsAccess);
Map<String, dynamic> holes =
{'needsClassSupport': emitter.needsClassSupport,
'libraries': librariesAccess,
'mangledNames': mangledNamesAccess,
'mangledGlobalNames': mangledGlobalNamesAccess,
'statics': staticsAccess,
'typeInformation': typeInformationAccess,
'globalFunctions': globalFunctionsAccess,
'enabledInvokeOn': compiler.enabledInvokeOn,
'interceptedNames': interceptedNamesAccess,
'interceptedNamesSet': emitter.generateInterceptedNamesSet(),
'notInCspMode': !compiler.useContentSecurityPolicy,
'inCspMode': compiler.useContentSecurityPolicy,
'deferredAction': namer.deferredAction,
'hasIsolateSupport': program.hasIsolateSupport,
'fieldNamesProperty': js.string(OldEmitter.FIELD_NAMES_PROPERTY_NAME),
'hasIncrementalSupport': compiler.hasIncrementalSupport,
'incrementalHelper': namer.accessIncrementalHelper,
'createNewIsolateFunction': createNewIsolateFunctionAccess,
'isolateName': namer.isolateName,
'classIdExtractor': classIdExtractorAccess,
'classFieldsExtractor': classFieldsExtractorAccess,
'instanceFromClassId': instanceFromClassIdAccess,
'initializeEmptyInstance': initializeEmptyInstanceAccess,
'allClasses': allClassesAccess,
'debugFastObjects': DEBUG_FAST_OBJECTS,
'isTreeShakingDisabled': backend.isTreeShakingDisabled,
'precompiled': precompiledAccess,
'finishedClassesAccess': finishedClassesAccess,
'markerFun': emitter.markerFun,
'needsMixinSupport': emitter.needsMixinSupport,
'needsNativeSupport': program.needsNativeSupport,
'isInterceptorClass': namer.operatorIs(backend.jsInterceptorClass),
'isObject' : namer.operatorIs(compiler.objectClass),
'specProperty': js.string(namer.nativeSpecProperty),
'trivialNsmHandlers': emitter.buildTrivialNsmHandlers(),
'hasRetainedMetadata': backend.hasRetainedMetadata,
'types': typesAccess,
'objectClassName': js.string(namer.runtimeTypeName(compiler.objectClass)),
'needsStructuredMemberInfo': emitter.needsStructuredMemberInfo,
'usesMangledNames':
compiler.mirrorsLibrary != null || compiler.enabledFunctionApply,
'tearOffCode': buildTearOffCode(backend),
'nativeInfoHandler': nativeInfoHandler,
'operatorIsPrefix' : js.string(namer.operatorIsPrefix),
'deferredActionString': js.string(namer.deferredAction)};
String skeleton = '''
function $setupProgramName(programData) {
"use strict";
if (#needsClassSupport) {
function generateAccessor(fieldDescriptor, accessors, cls) {
var fieldInformation = fieldDescriptor.split("-");
var field = fieldInformation[0];
var len = field.length;
var code = field.charCodeAt(len - 1);
var reflectable;
if (fieldInformation.length > 1) reflectable = true;
else reflectable = false;
code = ((code >= $RANGE1_FIRST) && (code <= $RANGE1_LAST))
? code - $RANGE1_ADJUST
: ((code >= $RANGE2_FIRST) && (code <= $RANGE2_LAST))
? code - $RANGE2_ADJUST
: ((code >= $RANGE3_FIRST) && (code <= $RANGE3_LAST))
? code - $RANGE3_ADJUST
: $NO_FIELD_CODE;
if (code) { // needsAccessor
var getterCode = code & 3;
var setterCode = code >> 2;
var accessorName = field = field.substring(0, len - 1);
var divider = field.indexOf(":");
if (divider > 0) { // Colon never in first position.
accessorName = field.substring(0, divider);
field = field.substring(divider + 1);
}
if (getterCode) { // needsGetter
var args = (getterCode & 2) ? "$receiverParamName" : "";
var receiver = (getterCode & 1) ? "this" : "$receiverParamName";
var body = "return " + receiver + "." + field;
var property =
cls + ".prototype.${namer.getterPrefix}" + accessorName + "=";
var fn = "function(" + args + "){" + body + "}";
if (reflectable)
accessors.push(property + "\$reflectable(" + fn + ");\\n");
else
accessors.push(property + fn + ";\\n");
}
if (setterCode) { // needsSetter
var args = (setterCode & 2)
? "$receiverParamName,${_}$valueParamName"
: "$valueParamName";
var receiver = (setterCode & 1) ? "this" : "$receiverParamName";
var body = receiver + "." + field + "$_=$_$valueParamName";
var property =
cls + ".prototype.${namer.setterPrefix}" + accessorName + "=";
var fn = "function(" + args + "){" + body + "}";
if (reflectable)
accessors.push(property + "\$reflectable(" + fn + ");\\n");
else
accessors.push(property + fn + ";\\n");
}
}
return field;
}
// First the class name, then the field names in an array and the members
// (inside an Object literal).
// The caller can also pass in the constructor as a function if needed.
//
// Example:
// defineClass("A", ["x", "y"], {
// foo\$1: function(y) {
// print(this.x + y);
// },
// bar\$2: function(t, v) {
// this.x = t - v;
// },
// });
function defineClass(name, fields) {
var accessors = [];
var str = "function " + name + "(";
var body = "";
if (#hasIsolateSupport) { var fieldNames = ""; }
for (var i = 0; i < fields.length; i++) {
if(i != 0) str += ", ";
var field = generateAccessor(fields[i], accessors, name);
if (#hasIsolateSupport) { fieldNames += "'" + field + "',"; }
var parameter = "p_" + field;
str += parameter;
body += ("this." + field + " = " + parameter + ";\\n");
}
if (supportsDirectProtoAccess) {
body += "this." + #deferredActionString + "();";
}
str += ") {\\n" + body + "}\\n";
str += name + ".builtin\$cls=\\"" + name + "\\";\\n";
str += "\$desc=\$collectedClasses." + name + "[1];\\n";
str += name + ".prototype = \$desc;\\n";
if (typeof defineClass.name != "string") {
str += name + ".name=\\"" + name + "\\";\\n";
}
if (#hasIsolateSupport) {
str += name + "." + #fieldNamesProperty + "=[" + fieldNames
+ "];\\n";
}
str += accessors.join("");
return str;
}
if (#hasIncrementalSupport) {
#incrementalHelper.defineClass = defineClass;
}
if (#hasIsolateSupport) {
#createNewIsolateFunction = function() { return new #isolateName(); };
#classIdExtractor = function(o) { return o.constructor.name; };
#classFieldsExtractor = function(o) {
var fieldNames = o.constructor.#fieldNamesProperty;
if (!fieldNames) return []; // TODO(floitsch): do something else here.
var result = [];
result.length = fieldNames.length;
for (var i = 0; i < fieldNames.length; i++) {
result[i] = o[fieldNames[i]];
}
return result;
};
#instanceFromClassId = function(name) { return new #allClasses[name](); };
#initializeEmptyInstance = function(name, o, fields) {
#allClasses[name].apply(o, fields);
return o; //
}
}
// If the browser supports changing the prototype via __proto__, we make
// use of that feature. Otherwise, we copy the properties into a new
// constructor.
var inheritFrom = supportsDirectProtoAccess ?
function(constructor, superConstructor) {
var prototype = constructor.prototype;
prototype.__proto__ = superConstructor.prototype;
// Use a function for `true` here, as functions are stored in the
// hidden class and not as properties in the object.
prototype.constructor = constructor;
prototype[#operatorIsPrefix + constructor.name] = constructor;
return convertToFastObject(prototype);
} :
function() {
function tmp() {}
return function (constructor, superConstructor) {
tmp.prototype = superConstructor.prototype;
var object = new tmp();
convertToSlowObject(object);
var properties = constructor.prototype;
var members = Object.keys(properties);
for (var i = 0; i < members.length; i++) {
var member = members[i];
object[member] = properties[member];
}
// Use a function for `true` here, as functions are stored in the
// hidden class and not as properties in the object.
object[#operatorIsPrefix + constructor.name] = constructor;
object.constructor = constructor;
constructor.prototype = object;
return object;
};
}();
if (#hasIncrementalSupport) {
#incrementalHelper.inheritFrom = inheritFrom;
}
// Class descriptions are collected in a JS object.
// 'finishClasses' takes all collected descriptions and sets up
// the prototype.
// Once set up, the constructors prototype field satisfy:
// - it contains all (local) members.
// - its internal prototype (__proto__) points to the superclass'
// prototype field.
// - the prototype's constructor field points to the JavaScript
// constructor.
// For engines where we have access to the '__proto__' we can manipulate
// the object literal directly. For other engines we have to create a new
// object and copy over the members.
function finishClasses(processedClasses) {
if (#debugFastObjects)
print("Number of classes: " +
Object.getOwnPropertyNames(processedClasses.collected).length);
var allClasses = #allClasses;
if (#inCspMode) {
var constructors = #precompiled(processedClasses.collected);
}
if (#notInCspMode) {
processedClasses.combinedConstructorFunction +=
"return [\\n" + processedClasses.constructorsList.join(",\\n ") +
"\\n]";
var constructors =
new Function("\$collectedClasses",
processedClasses.combinedConstructorFunction)
(processedClasses.collected);
processedClasses.combinedConstructorFunction = null;
}
for (var i = 0; i < constructors.length; i++) {
var constructor = constructors[i];
var cls = constructor.name;
var desc = processedClasses.collected[cls];
var globalObject = desc[0];
desc = desc[1];
if (#isTreeShakingDisabled)
constructor["${namer.metadataField}"] = desc;
allClasses[cls] = constructor;
globalObject[cls] = constructor;
}
constructors = null;
var finishedClasses = #finishedClassesAccess;
function finishClass(cls) {
if (finishedClasses[cls]) return;
finishedClasses[cls] = true;
var superclass = processedClasses.pending[cls];
if (#needsMixinSupport) {
if (superclass && superclass.indexOf("+") > 0) {
var s = superclass.split("+");
superclass = s[0];
var mixinClass = s[1];
finishClass(mixinClass);
var mixin = allClasses[mixinClass];
var mixinPrototype = mixin.prototype;
var clsPrototype = allClasses[cls].prototype;
var properties = Object.keys(mixinPrototype);
for (var i = 0; i < properties.length; i++) {
var d = properties[i];
if (!hasOwnProperty.call(clsPrototype, d))
clsPrototype[d] = mixinPrototype[d];
}
}
}
// The superclass is only false (empty string) for the Dart Object
// class. The minifier together with noSuchMethod can put methods on
// the Object.prototype object, and they show through here, so we check
// that we have a string.
if (!superclass || typeof superclass != "string") {
// Inlined special case of InheritFrom here for performance reasons.
// Fix up the the Dart Object class' prototype.
var constructor = allClasses[cls];
var prototype = constructor.prototype;
prototype.constructor = constructor;
prototype.#isObject = constructor;
prototype.#deferredAction = #markerFun;
return;
}
finishClass(superclass);
var superConstructor = allClasses[superclass];
if (!superConstructor) {
superConstructor = existingIsolateProperties[superclass];
}
var constructor = allClasses[cls];
var prototype = inheritFrom(constructor, superConstructor);
if (#needsNativeSupport) {
if (Object.prototype.hasOwnProperty.call(prototype, #specProperty)) {
#nativeInfoHandler;
// As native classes can come into existence without a constructor
// call, we have to ensure that the class has been fully
// initialized.
if (constructor.prototype.#deferredAction)
finishAddStubsHelper(constructor.prototype);
}
}
// Interceptors (or rather their prototypes) are also used without
// first instantiating them first.
if (prototype.#isInterceptorClass &&
constructor.prototype.#deferredAction) {
finishAddStubsHelper(constructor.prototype);
}
}
#trivialNsmHandlers;
var properties = Object.keys(processedClasses.pending);
for (var i = 0; i < properties.length; i++) finishClass(properties[i]);
}
// For convenience, this method can be called with a prototype as argument
// or, if it was bound to an object, by invoking it as a method. Therefore,
// if prototype is undefined, this is used as prototype.
function finishAddStubsHelper(prototype) {
var prototype = prototype || this;
var object;
while (prototype.#deferredAction != #markerFun) {
if (prototype.hasOwnProperty(#deferredActionString)) {
delete prototype.#deferredAction; // Intended to make it slow, too.
var properties = Object.keys(prototype);
for (var index = 0; index < properties.length; index++) {
var property = properties[index];
var firstChar = property.charCodeAt(0);
var elem;
// We have to filter out some special properties that are used for
// metadata in descriptors. Currently, we filter everything that
// starts with + or *. This has to stay in sync with the special
// properties that are used by processClassData below.
if (property !== "${namer.classDescriptorProperty}" &&
property !== "$reflectableField" &&
firstChar !== 43 && // 43 is aka "+".
firstChar !== 42 && // 42 is aka "*"
(elem = prototype[property]) != null &&
elem.constructor === Array &&
property !== "<>") {
addStubs(prototype, elem, property, false, []);
}
}
convertToFastObject(prototype);
}
prototype = prototype.__proto__;
}
}
function processClassData(cls, descriptor, processedClasses) {
descriptor = convertToSlowObject(descriptor); // Use a slow object.
var previousProperty;
var properties = Object.keys(descriptor);
var hasDeferredWork = false;
var shouldDeferWork =
supportsDirectProtoAccess && cls != #objectClassName;
for (var i = 0; i < properties.length; i++) {
var property = properties[i];
var firstChar = property.charCodeAt(0);
if (property === "static") {
processStatics(#statics[cls] = descriptor.static,
processedClasses);
delete descriptor.static;
} else if (firstChar === 43) { // 43 is "+".
mangledNames[previousProperty] = property.substring(1);
var flag = descriptor[property];
if (flag > 0)
descriptor[previousProperty].$reflectableField = flag;
} else if (firstChar === 42) { // 42 is "*"
descriptor[previousProperty].$defaultValuesField =
descriptor[property];
var optionalMethods = descriptor.$methodsWithOptionalArgumentsField;
if (!optionalMethods) {
descriptor.$methodsWithOptionalArgumentsField = optionalMethods={}
}
optionalMethods[property] = previousProperty;
} else {
var elem = descriptor[property];
if (property !== "${namer.classDescriptorProperty}" &&
elem != null &&
elem.constructor === Array &&
property !== "<>") {
if (shouldDeferWork) {
hasDeferredWork = true;
} else {
addStubs(descriptor, elem, property, false, []);
}
} else {
previousProperty = property;
}
}
}
if (hasDeferredWork)
descriptor.#deferredAction = finishAddStubsHelper;
/* The 'fields' are either a constructor function or a
* string encoding fields, constructor and superclass. Gets the
* superclass and fields in the format
* 'Super;field1,field2'
* from the CLASS_DESCRIPTOR_PROPERTY property on the descriptor.
*/
var classData = descriptor["${namer.classDescriptorProperty}"],
split, supr, fields = classData;
if (#hasRetainedMetadata)
if (typeof classData == "object" &&
classData instanceof Array) {
classData = fields = classData[0];
}
// ${ClassBuilder.fieldEncodingDescription}.
var s = fields.split(";");
fields = s[1] == "" ? [] : s[1].split(",");
supr = s[0];
// ${ClassBuilder.functionTypeEncodingDescription}.
split = supr.split(":");
if (split.length == 2) {
supr = split[0];
var functionSignature = split[1];
if (functionSignature)
descriptor.${namer.operatorSignature} = function(s) {
return function() {
return #types[s];
};
}(functionSignature);
}
if (supr) processedClasses.pending[cls] = supr;
if (#notInCspMode) {
processedClasses.combinedConstructorFunction +=
defineClass(cls, fields);
processedClasses.constructorsList.push(cls);
}
processedClasses.collected[cls] = [globalObject, descriptor];
classes.push(cls);
}
}
function processStatics(descriptor, processedClasses) {
var properties = Object.keys(descriptor);
for (var i = 0; i < properties.length; i++) {
var property = properties[i];
if (property === "${namer.classDescriptorProperty}") continue;
var element = descriptor[property];
var firstChar = property.charCodeAt(0);
var previousProperty;
if (firstChar === 43) { // 43 is "+".
mangledGlobalNames[previousProperty] = property.substring(1);
var flag = descriptor[property];
if (flag > 0)
descriptor[previousProperty].$reflectableField = flag;
if (element && element.length)
#typeInformation[previousProperty] = element;
} else if (firstChar === 42) { // 42 is "*"
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);
#globalFunctions[property] = element;
} else if (element.constructor === Array) {
if (#needsStructuredMemberInfo) {
addStubs(globalObject, element, property, true, functions);
}
} else {
// We will not enter this case if no classes are defined.
if (#needsClassSupport) {
previousProperty = property;
processClassData(property, element, processedClasses);
}
}
}
}
if (#needsStructuredMemberInfo) {
// See [dart2js.js_emitter.ContainerBuilder.addMemberMethod] for format of
// [array].
// Processes the stub declaration given by [array] and stores the results
// in the corresponding [prototype]. [name] is the property name in
// [prototype] that the stub declaration belongs to.
// If [isStatic] is true, the property being processed belongs to a static
// function and thus is stored as a global. In that case we also add all
// generated functions to the [functions] array, which is used by the
// mirrors system to enumerate all static functions of a library. For
// non-static functions we might still add some functions to [functions] but
// the information is thrown away at the call site. This is to avoid
// conditionals.
function addStubs(prototype, array, name, isStatic, functions) {
var index = $FUNCTION_INDEX, alias = array[index], f;
if (typeof alias == "string") {
f = array[++index];
} else {
f = alias;
alias = name;
}
var funcs = [prototype[name] = prototype[alias] = f];
f.\$stubName = name;
functions.push(name);
for (; index < array.length; index += 2) {
f = array[index + 1];
if (typeof f != "function") break;
f.\$stubName = ${readString("array", "index + 2")};
funcs.push(f);
if (f.\$stubName) {
prototype[f.\$stubName] = f;
functions.push(f.\$stubName);
}
}
index++;
for (var i = 0; i < funcs.length; index++, i++) {
funcs[i].\$callName = ${readString("array", "index")};
}
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 isIntercepted =
requiredParameterCount + optionalParameterCount != funcs[0].length;
var functionTypeIndex = ${readFunctionType("array", "2")};
var unmangledNameIndex = $unmangledNameIndex;
if (getterStubName) {
f = tearOff(funcs, array, isStatic, name, isIntercepted);
prototype[name].\$getter = f;
f.\$getterStub = true;
// Used to create an isolate using spawnFunction.
if (isStatic) {
#globalFunctions[name] = f;
functions.push(getterStubName);
}
prototype[getterStubName] = f;
funcs.push(f);
f.\$stubName = getterStubName;
f.\$callName = null;
// Update the interceptedNames map (which only exists if `invokeOn` was
// enabled).
if (#enabledInvokeOn)
if (isIntercepted) #interceptedNames[getterStubName] = 1;
}
if (#usesMangledNames) {
var isReflectable = array.length > unmangledNameIndex;
if (isReflectable) {
for (var i = 0; i < funcs.length; i++) {
funcs[i].$reflectableField = 1;
funcs[i].$reflectionInfoField = array;
}
var mangledNames = isStatic ? #mangledGlobalNames : #mangledNames;
var unmangledName = ${readString("array", "unmangledNameIndex")};
// The function is either a getter, a setter, or a method.
// If it is a method, it might also have a tear-off closure.
// The unmangledName is the same as the getter-name.
var reflectionName = unmangledName;
if (getterStubName) mangledNames[getterStubName] = reflectionName;
if (isSetter) {
reflectionName += "=";
} else if (!isGetter) {
reflectionName += ":" + requiredParameterCount +
":" + optionalParameterCount;
}
mangledNames[name] = reflectionName;
funcs[0].$reflectionNameField = reflectionName;
funcs[0].$metadataIndexField = unmangledNameIndex + 1;
if (optionalParameterCount) prototype[unmangledName + "*"] = funcs[0];
}
}
}
#tearOffCode;
}
if (#hasIncrementalSupport) {
#incrementalHelper.addStubs = addStubs;
}
var functionCounter = 0;
if (!#libraries) #libraries = [];
if (!#mangledNames) #mangledNames = map();
if (!#mangledGlobalNames) #mangledGlobalNames = map();
if (!#statics) #statics = map();
if (!#typeInformation) #typeInformation = map();
if (!#globalFunctions) #globalFunctions = map();
if (#enabledInvokeOn)
if (!#interceptedNames) #interceptedNames = #interceptedNamesSet;
var libraries = #libraries;
var mangledNames = #mangledNames;
var mangledGlobalNames = #mangledGlobalNames;
var hasOwnProperty = Object.prototype.hasOwnProperty;
var length = programData.length;
var processedClasses = map();
processedClasses.collected = map();
processedClasses.pending = map();
if (#notInCspMode) {
processedClasses.constructorsList = [];
// For every class processed [processedClasses.combinedConstructorFunction]
// will be updated with the corresponding constructor function.
processedClasses.combinedConstructorFunction =
"function \$reflectable(fn){fn.$reflectableField=1;return fn};\\n"+
"var \$desc;\\n";
}
for (var i = 0; i < length; i++) {
var data = programData[i];
// [data] contains these elements:
// 0. The library name (not unique).
// 1. The library URI (unique).
// 2. A function returning the metadata associated with this library.
// 3. The global object to use for this library.
// 4. An object literal listing the members of the library.
// 5. This element is optional and if present it is true and signals that this
// library is the root library (see dart:mirrors IsolateMirror.rootLibrary).
//
// The entries of [data] are built in [assembleProgram] above.
var name = data[0];
var uri = data[1];
var metadata = data[2];
var globalObject = data[3];
var descriptor = data[4];
var isRoot = !!data[5];
var fields = descriptor && descriptor["${namer.classDescriptorProperty}"];
if (fields instanceof Array) fields = fields[0];
var classes = [];
var functions = [];
processStatics(descriptor, processedClasses);
libraries.push([name, uri, classes, functions, metadata, fields, isRoot,
globalObject]);
}
if (#needsClassSupport) finishClasses(processedClasses);
}''';
// TODO(zarah): Remove empty else branches in output when if(#hole) is false.
return js.statement(skeleton, holes);
}
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 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) {
if (!VALIDATE_DATA) return '$array[$index]';
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;
})()''';
}