| Index: pkg/compiler/lib/src/js_emitter/old_emitter/emitter.dart
|
| diff --git a/pkg/compiler/lib/src/js_emitter/old_emitter/emitter.dart b/pkg/compiler/lib/src/js_emitter/old_emitter/emitter.dart
|
| deleted file mode 100644
|
| index 70114db83d3ef372bcd68030a09c369e5ee999dc..0000000000000000000000000000000000000000
|
| --- a/pkg/compiler/lib/src/js_emitter/old_emitter/emitter.dart
|
| +++ /dev/null
|
| @@ -1,2017 +0,0 @@
|
| -// Copyright (c) 2014, 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 OldEmitter implements Emitter {
|
| - final Compiler compiler;
|
| - final CodeEmitterTask task;
|
| -
|
| - final ContainerBuilder containerBuilder = new ContainerBuilder();
|
| - final ClassEmitter classEmitter = new ClassEmitter();
|
| - final NsmEmitter nsmEmitter = new NsmEmitter();
|
| - final InterceptorEmitter interceptorEmitter = new InterceptorEmitter();
|
| -
|
| - // TODO(johnniwinther): Wrap these fields in a caching strategy.
|
| - final Set<ConstantValue> cachedEmittedConstants;
|
| - final List<jsAst.Statement> cachedEmittedConstantsAst = <jsAst.Statement>[];
|
| - final Map<Element, ClassBuilder> cachedClassBuilders;
|
| - final Set<Element> cachedElements;
|
| -
|
| - bool needsClassSupport = false;
|
| - bool needsMixinSupport = false;
|
| - bool needsLazyInitializer = false;
|
| -
|
| - /// True if [ContainerBuilder.addMemberMethodFromInfo] used "structured info",
|
| - /// that is, some function was needed for reflection, had stubs, or had a
|
| - /// super alias.
|
| - bool needsStructuredMemberInfo = false;
|
| -
|
| - final Namer namer;
|
| - ConstantEmitter constantEmitter;
|
| - NativeEmitter get nativeEmitter => task.nativeEmitter;
|
| - TypeTestRegistry get typeTestRegistry => task.typeTestRegistry;
|
| -
|
| - // The full code that is written to each hunk part-file.
|
| - Map<OutputUnit, CodeOutput> outputBuffers = new Map<OutputUnit, CodeOutput>();
|
| -
|
| - String classesCollector;
|
| - Set<ClassElement> get neededClasses => task.neededClasses;
|
| - Map<OutputUnit, List<ClassElement>> get outputClassLists
|
| - => task.outputClassLists;
|
| - Map<OutputUnit, List<ConstantValue>> get outputConstantLists
|
| - => task.outputConstantLists;
|
| - final Map<jsAst.Name, String> mangledFieldNames =
|
| - new HashMap<jsAst.Name, String>();
|
| - final Map<jsAst.Name, String> mangledGlobalFieldNames =
|
| - new HashMap<jsAst.Name, String>();
|
| - final Set<jsAst.Name> recordedMangledNames = new Set<jsAst.Name>();
|
| -
|
| - List<TypedefElement> get typedefsNeededForReflection =>
|
| - task.typedefsNeededForReflection;
|
| -
|
| - JavaScriptBackend get backend => compiler.backend;
|
| - TypeVariableHandler get typeVariableHandler => backend.typeVariableHandler;
|
| -
|
| - String get _ => space;
|
| - String get space => compiler.enableMinification ? "" : " ";
|
| - String get n => compiler.enableMinification ? "" : "\n";
|
| - String get N => compiler.enableMinification ? "\n" : ";\n";
|
| -
|
| - /**
|
| - * List of expressions and statements that will be included in the
|
| - * precompiled function.
|
| - *
|
| - * To save space, dart2js normally generates constructors and accessors
|
| - * dynamically. This doesn't work in CSP mode, so dart2js emits them directly
|
| - * when in CSP mode.
|
| - */
|
| - Map<OutputUnit, List<jsAst.Node>> _cspPrecompiledFunctions =
|
| - new Map<OutputUnit, List<jsAst.Node>>();
|
| -
|
| - Map<OutputUnit, List<jsAst.Expression>> _cspPrecompiledConstructorNames =
|
| - new Map<OutputUnit, List<jsAst.Expression>>();
|
| -
|
| - /**
|
| - * Accumulate properties for classes and libraries, describing their
|
| - * static/top-level members.
|
| - * Later, these members are emitted when the class or library is emitted.
|
| - *
|
| - * See [getElementDescriptor].
|
| - */
|
| - // TODO(ahe): Generate statics with their class, and store only libraries in
|
| - // this map.
|
| - final Map<Fragment, Map<Element, ClassBuilder>> elementDescriptors =
|
| - new Map<Fragment, Map<Element, ClassBuilder>>();
|
| -
|
| - final bool generateSourceMap;
|
| -
|
| - OldEmitter(Compiler compiler, Namer namer, this.generateSourceMap, this.task)
|
| - : this.compiler = compiler,
|
| - this.namer = namer,
|
| - cachedEmittedConstants = compiler.cacheStrategy.newSet(),
|
| - cachedClassBuilders = compiler.cacheStrategy.newMap(),
|
| - cachedElements = compiler.cacheStrategy.newSet() {
|
| - constantEmitter = new ConstantEmitter(
|
| - compiler, namer, this.constantReference, constantListGenerator);
|
| - containerBuilder.emitter = this;
|
| - classEmitter.emitter = this;
|
| - nsmEmitter.emitter = this;
|
| - interceptorEmitter.emitter = this;
|
| - }
|
| -
|
| - List<jsAst.Node> cspPrecompiledFunctionFor(OutputUnit outputUnit) {
|
| - return _cspPrecompiledFunctions.putIfAbsent(
|
| - outputUnit,
|
| - () => new List<jsAst.Node>());
|
| - }
|
| -
|
| - List<jsAst.Expression> cspPrecompiledConstructorNamesFor(
|
| - OutputUnit outputUnit) {
|
| - return _cspPrecompiledConstructorNames.putIfAbsent(
|
| - outputUnit,
|
| - () => new List<jsAst.Expression>());
|
| - }
|
| -
|
| - /// Erases the precompiled information for csp mode for all output units.
|
| - /// Used by the incremental compiler.
|
| - void clearCspPrecompiledNodes() {
|
| - _cspPrecompiledFunctions.clear();
|
| - _cspPrecompiledConstructorNames.clear();
|
| - }
|
| -
|
| - @override
|
| - bool isConstantInlinedOrAlreadyEmitted(ConstantValue constant) {
|
| - if (constant.isFunction) return true; // Already emitted.
|
| - if (constant.isPrimitive) return true; // Inlined.
|
| - if (constant.isDummy) return true; // Inlined.
|
| - // The name is null when the constant is already a JS constant.
|
| - // TODO(floitsch): every constant should be registered, so that we can
|
| - // share the ones that take up too much space (like some strings).
|
| - if (namer.constantName(constant) == null) return true;
|
| - return false;
|
| - }
|
| -
|
| - @override
|
| - int compareConstants(ConstantValue a, ConstantValue b) {
|
| - // Inlined constants don't affect the order and sometimes don't even have
|
| - // names.
|
| - int cmp1 = isConstantInlinedOrAlreadyEmitted(a) ? 0 : 1;
|
| - int cmp2 = isConstantInlinedOrAlreadyEmitted(b) ? 0 : 1;
|
| - if (cmp1 + cmp2 < 2) return cmp1 - cmp2;
|
| -
|
| - // Emit constant interceptors first. Constant interceptors for primitives
|
| - // might be used by code that builds other constants. See Issue 18173.
|
| - if (a.isInterceptor != b.isInterceptor) {
|
| - return a.isInterceptor ? -1 : 1;
|
| - }
|
| -
|
| - // Sorting by the long name clusters constants with the same constructor
|
| - // which compresses a tiny bit better.
|
| - int r = namer.constantLongName(a).compareTo(namer.constantLongName(b));
|
| - if (r != 0) return r;
|
| - // Resolve collisions in the long name by using the constant name (i.e. JS
|
| - // name) which is unique.
|
| - // TODO(herhut): Find a better way to resolve collisions.
|
| - return namer.constantName(a).hashCode.compareTo(
|
| - namer.constantName(b).hashCode);
|
| - }
|
| -
|
| - @override
|
| - jsAst.Expression constantReference(ConstantValue value) {
|
| - if (value.isFunction) {
|
| - FunctionConstantValue functionConstant = value;
|
| - return isolateStaticClosureAccess(functionConstant.element);
|
| - }
|
| -
|
| - // We are only interested in the "isInlined" part, but it does not hurt to
|
| - // test for the other predicates.
|
| - if (isConstantInlinedOrAlreadyEmitted(value)) {
|
| - return constantEmitter.generate(value);
|
| - }
|
| - return js('#.#', [namer.globalObjectForConstant(value),
|
| - namer.constantName(value)]);
|
| - }
|
| -
|
| - jsAst.Expression constantInitializerExpression(ConstantValue value) {
|
| - return constantEmitter.generate(value);
|
| - }
|
| -
|
| - String get name => 'CodeEmitter';
|
| -
|
| - String get finishIsolateConstructorName
|
| - => '${namer.isolateName}.\$finishIsolateConstructor';
|
| - String get isolatePropertiesName
|
| - => '${namer.isolateName}.${namer.isolatePropertiesName}';
|
| - String get lazyInitializerProperty
|
| - => r'$lazy';
|
| - String get lazyInitializerName
|
| - => '${namer.isolateName}.${lazyInitializerProperty}';
|
| - String get initName => 'init';
|
| -
|
| - jsAst.Name get makeConstListProperty
|
| - => namer.internalGlobal('makeConstantList');
|
| -
|
| - /// The name of the property that contains all field names.
|
| - ///
|
| - /// This property is added to constructors when isolate support is enabled.
|
| - static const String FIELD_NAMES_PROPERTY_NAME = r"$__fields__";
|
| -
|
| - /// For deferred loading we communicate the initializers via this global var.
|
| - final String deferredInitializers = r"$dart_deferred_initializers$";
|
| -
|
| - /// Contains the global state that is needed to initialize and load a
|
| - /// deferred library.
|
| - String get globalsHolder => r"$globals$";
|
| -
|
| - @override
|
| - jsAst.Expression generateEmbeddedGlobalAccess(String global) {
|
| - return js(generateEmbeddedGlobalAccessString(global));
|
| - }
|
| -
|
| - String generateEmbeddedGlobalAccessString(String global) {
|
| - // TODO(floitsch): don't use 'init' as global embedder storage.
|
| - return '$initName.$global';
|
| - }
|
| -
|
| - jsAst.PropertyAccess globalPropertyAccess(Element element) {
|
| - jsAst.Name name = namer.globalPropertyName(element);
|
| - jsAst.PropertyAccess pa = new jsAst.PropertyAccess(
|
| - new jsAst.VariableUse(namer.globalObjectFor(element)),
|
| - name);
|
| - return pa;
|
| - }
|
| -
|
| - @override
|
| - jsAst.Expression isolateLazyInitializerAccess(FieldElement element) {
|
| - return jsAst.js('#.#', [namer.globalObjectFor(element),
|
| - namer.lazyInitializerName(element)]);
|
| - }
|
| -
|
| - @override
|
| - jsAst.Expression isolateStaticClosureAccess(FunctionElement element) {
|
| - return jsAst.js('#.#()',
|
| - [namer.globalObjectFor(element), namer.staticClosureName(element)]);
|
| - }
|
| -
|
| - @override
|
| - jsAst.PropertyAccess staticFieldAccess(FieldElement element) {
|
| - return globalPropertyAccess(element);
|
| - }
|
| -
|
| - @override
|
| - jsAst.PropertyAccess staticFunctionAccess(FunctionElement element) {
|
| - return globalPropertyAccess(element);
|
| - }
|
| -
|
| - @override
|
| - jsAst.PropertyAccess constructorAccess(ClassElement element) {
|
| - return globalPropertyAccess(element);
|
| - }
|
| -
|
| - @override
|
| - jsAst.PropertyAccess prototypeAccess(ClassElement element,
|
| - bool hasBeenInstantiated) {
|
| - return jsAst.js('#.prototype', constructorAccess(element));
|
| - }
|
| -
|
| - @override
|
| - jsAst.PropertyAccess interceptorClassAccess(ClassElement element) {
|
| - return globalPropertyAccess(element);
|
| - }
|
| -
|
| - @override
|
| - jsAst.PropertyAccess typeAccess(Element element) {
|
| - return globalPropertyAccess(element);
|
| - }
|
| -
|
| - @override
|
| - jsAst.Template templateForBuiltin(JsBuiltin builtin) {
|
| - switch (builtin) {
|
| - case JsBuiltin.dartObjectConstructor:
|
| - return jsAst.js.expressionTemplateYielding(
|
| - typeAccess(compiler.objectClass));
|
| -
|
| - case JsBuiltin.isCheckPropertyToJsConstructorName:
|
| - int isPrefixLength = namer.operatorIsPrefix.length;
|
| - return jsAst.js.expressionTemplateFor('#.substring($isPrefixLength)');
|
| -
|
| - case JsBuiltin.isFunctionType:
|
| - return backend.rti.representationGenerator.templateForIsFunctionType;
|
| -
|
| - case JsBuiltin.rawRtiToJsConstructorName:
|
| - return jsAst.js.expressionTemplateFor("#.$typeNameProperty");
|
| -
|
| - case JsBuiltin.rawRuntimeType:
|
| - return jsAst.js.expressionTemplateFor("#.constructor");
|
| -
|
| - case JsBuiltin.createFunctionTypeRti:
|
| - return backend.rti.representationGenerator
|
| - .templateForCreateFunctionType;
|
| -
|
| - case JsBuiltin.isSubtype:
|
| - // TODO(floitsch): move this closer to where is-check properties are
|
| - // built.
|
| - String isPrefix = namer.operatorIsPrefix;
|
| - return jsAst.js.expressionTemplateFor(
|
| - "('$isPrefix' + #) in #.prototype");
|
| -
|
| - case JsBuiltin.isGivenTypeRti:
|
| - return jsAst.js.expressionTemplateFor('#.$typeNameProperty === #');
|
| -
|
| - case JsBuiltin.getMetadata:
|
| - String metadataAccess =
|
| - generateEmbeddedGlobalAccessString(embeddedNames.METADATA);
|
| - return jsAst.js.expressionTemplateFor("$metadataAccess[#]");
|
| -
|
| - case JsBuiltin.getType:
|
| - String typesAccess =
|
| - generateEmbeddedGlobalAccessString(embeddedNames.TYPES);
|
| - return jsAst.js.expressionTemplateFor("$typesAccess[#]");
|
| -
|
| - default:
|
| - compiler.internalError(NO_LOCATION_SPANNABLE,
|
| - "Unhandled Builtin: $builtin");
|
| - return null;
|
| - }
|
| - }
|
| -
|
| - List<jsAst.Statement> buildTrivialNsmHandlers(){
|
| - return nsmEmitter.buildTrivialNsmHandlers();
|
| - }
|
| -
|
| - jsAst.Statement buildNativeInfoHandler(
|
| - jsAst.Expression infoAccess,
|
| - jsAst.Expression constructorAccess,
|
| - jsAst.Expression subclassReadGenerator(jsAst.Expression subclass),
|
| - jsAst.Expression interceptorsByTagAccess,
|
| - jsAst.Expression leafTagsAccess) {
|
| - return NativeGenerator.buildNativeInfoHandler(infoAccess, constructorAccess,
|
| - subclassReadGenerator,
|
| - interceptorsByTagAccess,
|
| - leafTagsAccess);
|
| - }
|
| -
|
| - jsAst.ObjectInitializer generateInterceptedNamesSet() {
|
| - return interceptorEmitter.generateInterceptedNamesSet();
|
| - }
|
| -
|
| - /// In minified mode we want to keep the name for the most common core types.
|
| - bool _isNativeTypeNeedingReflectionName(Element element) {
|
| - if (!element.isClass) return false;
|
| - return (element == compiler.intClass ||
|
| - element == compiler.doubleClass ||
|
| - element == compiler.numClass ||
|
| - element == compiler.stringClass ||
|
| - element == compiler.boolClass ||
|
| - element == compiler.nullClass ||
|
| - element == compiler.listClass);
|
| - }
|
| -
|
| - /// Returns the "reflection name" of an [Element] or [Selector].
|
| - /// The reflection name of a getter 'foo' is 'foo'.
|
| - /// The reflection name of a setter 'foo' is 'foo='.
|
| - /// The reflection name of a method 'foo' is 'foo:N:M:O', where N is the
|
| - /// number of required arguments, M is the number of optional arguments, and
|
| - /// O is the named arguments.
|
| - /// The reflection name of a constructor is similar to a regular method but
|
| - /// starts with 'new '.
|
| - /// The reflection name of class 'C' is 'C'.
|
| - /// An anonymous mixin application has no reflection name.
|
| - /// This is used by js_mirrors.dart.
|
| - String getReflectionName(elementOrSelector, jsAst.Name mangledName) {
|
| - String name = elementOrSelector.name;
|
| - if (backend.shouldRetainName(name) ||
|
| - elementOrSelector is Element &&
|
| - // Make sure to retain names of unnamed constructors, and
|
| - // for common native types.
|
| - ((name == '' &&
|
| - backend.isAccessibleByReflection(elementOrSelector)) ||
|
| - _isNativeTypeNeedingReflectionName(elementOrSelector))) {
|
| -
|
| - // TODO(ahe): Enable the next line when I can tell the difference between
|
| - // an instance method and a global. They may have the same mangled name.
|
| - // if (recordedMangledNames.contains(mangledName)) return null;
|
| - recordedMangledNames.add(mangledName);
|
| - return getReflectionNameInternal(elementOrSelector, mangledName);
|
| - }
|
| - return null;
|
| - }
|
| -
|
| - String getReflectionNameInternal(elementOrSelector,
|
| - jsAst.Name mangledName) {
|
| - String name = namer.privateName(elementOrSelector.memberName);
|
| - if (elementOrSelector.isGetter) return name;
|
| - if (elementOrSelector.isSetter) {
|
| - if (mangledName is! SetterName) return '$name=';
|
| - SetterName setterName = mangledName;
|
| - jsAst.Name base = setterName.base;
|
| - jsAst.Name getter = namer.deriveGetterName(base);
|
| - mangledFieldNames.putIfAbsent(getter, () => name);
|
| - assert(mangledFieldNames[getter] == name);
|
| - recordedMangledNames.add(getter);
|
| - // TODO(karlklose,ahe): we do not actually need to store information
|
| - // about the name of this setter in the output, but it is needed for
|
| - // marking the function as invokable by reflection.
|
| - return '$name=';
|
| - }
|
| - if (elementOrSelector is Element && elementOrSelector.isClosure) {
|
| - // Closures are synthesized and their name might conflict with existing
|
| - // globals. Assign an illegal name, and make sure they don't clash
|
| - // with each other.
|
| - return " $name";
|
| - }
|
| - if (elementOrSelector is Selector
|
| - || elementOrSelector.isFunction
|
| - || elementOrSelector.isConstructor) {
|
| - int positionalParameterCount;
|
| - String namedArguments = '';
|
| - bool isConstructor = false;
|
| - if (elementOrSelector is Selector) {
|
| - CallStructure callStructure = elementOrSelector.callStructure;
|
| - positionalParameterCount = callStructure.positionalArgumentCount;
|
| - namedArguments = namedParametersAsReflectionNames(callStructure);
|
| - } else {
|
| - FunctionElement function = elementOrSelector;
|
| - if (function.isConstructor) {
|
| - isConstructor = true;
|
| - name = Elements.reconstructConstructorName(function);
|
| - }
|
| - FunctionSignature signature = function.functionSignature;
|
| - positionalParameterCount = signature.requiredParameterCount;
|
| - if (signature.optionalParametersAreNamed) {
|
| - var names = [];
|
| - for (Element e in signature.optionalParameters) {
|
| - names.add(e.name);
|
| - }
|
| - CallStructure callStructure =
|
| - new CallStructure(positionalParameterCount, names);
|
| - namedArguments = namedParametersAsReflectionNames(callStructure);
|
| - } else {
|
| - // Named parameters are handled differently by mirrors. For unnamed
|
| - // parameters, they are actually required if invoked
|
| - // reflectively. Also, if you have a method c(x) and c([x]) they both
|
| - // get the same mangled name, so they must have the same reflection
|
| - // name.
|
| - positionalParameterCount += signature.optionalParameterCount;
|
| - }
|
| - }
|
| - String suffix = '$name:$positionalParameterCount$namedArguments';
|
| - return (isConstructor) ? 'new $suffix' : suffix;
|
| - }
|
| - Element element = elementOrSelector;
|
| - if (element.isGenerativeConstructorBody) {
|
| - return null;
|
| - } else if (element.isClass) {
|
| - ClassElement cls = element;
|
| - if (cls.isUnnamedMixinApplication) return null;
|
| - return cls.name;
|
| - } else if (element.isTypedef) {
|
| - return element.name;
|
| - }
|
| - throw compiler.internalError(element,
|
| - 'Do not know how to reflect on this $element.');
|
| - }
|
| -
|
| - String namedParametersAsReflectionNames(CallStructure structure) {
|
| - if (structure.isUnnamed) return '';
|
| - String names = structure.getOrderedNamedArguments().join(':');
|
| - return ':$names';
|
| - }
|
| -
|
| - jsAst.Statement buildCspPrecompiledFunctionFor(
|
| - OutputUnit outputUnit) {
|
| - if (compiler.useContentSecurityPolicy) {
|
| - // TODO(ahe): Compute a hash code.
|
| - // TODO(sigurdm): Avoid this precompiled function. Generated
|
| - // constructor-functions and getter/setter functions can be stored in the
|
| - // library-description table. Setting properties on these can be moved to
|
| - // finishClasses.
|
| - return js.statement(r"""
|
| - #precompiled = function ($collectedClasses$) {
|
| - #norename;
|
| - var $desc;
|
| - #functions;
|
| - return #result;
|
| - };""",
|
| - {'norename': new jsAst.Comment("// ::norenaming:: "),
|
| - 'precompiled': generateEmbeddedGlobalAccess(embeddedNames.PRECOMPILED),
|
| - 'functions': cspPrecompiledFunctionFor(outputUnit),
|
| - 'result': new jsAst.ArrayInitializer(
|
| - cspPrecompiledConstructorNamesFor(outputUnit))});
|
| - } else {
|
| - return js.comment("Constructors are generated at runtime.");
|
| - }
|
| - }
|
| -
|
| - void assembleClass(Class cls, ClassBuilder enclosingBuilder,
|
| - Fragment fragment) {
|
| - ClassElement classElement = cls.element;
|
| - compiler.withCurrentElement(classElement, () {
|
| - if (compiler.hasIncrementalSupport) {
|
| - ClassBuilder cachedBuilder =
|
| - cachedClassBuilders.putIfAbsent(classElement, () {
|
| - ClassBuilder builder =
|
| - new ClassBuilder.forClass(classElement, namer);
|
| - classEmitter.emitClass(cls, builder, fragment);
|
| - return builder;
|
| - });
|
| - invariant(classElement, cachedBuilder.fields.isEmpty);
|
| - invariant(classElement, cachedBuilder.superName == null);
|
| - invariant(classElement, cachedBuilder.functionType == null);
|
| - invariant(classElement, cachedBuilder.fieldMetadata == null);
|
| - enclosingBuilder.properties.addAll(cachedBuilder.properties);
|
| - } else {
|
| - classEmitter.emitClass(cls, enclosingBuilder, fragment);
|
| - }
|
| - });
|
| - }
|
| -
|
| - void assembleStaticFunctions(Iterable<Method> staticFunctions,
|
| - Fragment fragment) {
|
| - if (staticFunctions == null) return;
|
| -
|
| - for (Method method in staticFunctions) {
|
| - Element element = method.element;
|
| - // We need to filter out null-elements for the interceptors.
|
| - // TODO(floitsch): use the precomputed interceptors here.
|
| - if (element == null) continue;
|
| - ClassBuilder builder = new ClassBuilder.forStatics(element, namer);
|
| - containerBuilder.addMemberMethod(method, builder);
|
| - getElementDescriptor(element, fragment).properties
|
| - .addAll(builder.properties);
|
| - }
|
| - }
|
| -
|
| - jsAst.Statement buildStaticNonFinalFieldInitializations(
|
| - OutputUnit outputUnit) {
|
| - jsAst.Statement buildInitialization(Element element,
|
| - jsAst.Expression initialValue) {
|
| - // Note: `namer.currentIsolate` refers to the isolate properties here.
|
| - return js.statement('${namer.currentIsolate}.# = #',
|
| - [namer.globalPropertyName(element), initialValue]);
|
| - }
|
| -
|
| - bool inMainUnit = (outputUnit == compiler.deferredLoadTask.mainOutputUnit);
|
| - JavaScriptConstantCompiler handler = backend.constants;
|
| - List<jsAst.Statement> parts = <jsAst.Statement>[];
|
| -
|
| - Iterable<Element> fields = task.outputStaticNonFinalFieldLists[outputUnit];
|
| - // If the outputUnit does not contain any static non-final fields, then
|
| - // [fields] is `null`.
|
| - if (fields != null) {
|
| - for (Element element in fields) {
|
| - compiler.withCurrentElement(element, () {
|
| - ConstantValue constant = handler.getInitialValueFor(element);
|
| - parts.add(buildInitialization(element, constantReference(constant)));
|
| - });
|
| - }
|
| - }
|
| -
|
| - if (inMainUnit && task.outputStaticNonFinalFieldLists.length > 1) {
|
| - // In the main output-unit we output a stub initializer for deferred
|
| - // variables, so that `isolateProperties` stays a fast object.
|
| - task.outputStaticNonFinalFieldLists.forEach(
|
| - (OutputUnit fieldsOutputUnit, Iterable<VariableElement> fields) {
|
| - if (fieldsOutputUnit == outputUnit) return; // Skip the main unit.
|
| - for (Element element in fields) {
|
| - compiler.withCurrentElement(element, () {
|
| - parts.add(buildInitialization(element, jsAst.number(0)));
|
| - });
|
| - }
|
| - });
|
| - }
|
| -
|
| - return new jsAst.Block(parts);
|
| - }
|
| -
|
| - jsAst.Statement buildLazilyInitializedStaticFields() {
|
| - JavaScriptConstantCompiler handler = backend.constants;
|
| - List<VariableElement> lazyFields =
|
| - handler.getLazilyInitializedFieldsForEmission();
|
| - if (lazyFields.isNotEmpty) {
|
| - needsLazyInitializer = true;
|
| - List<jsAst.Expression> laziesInfo = buildLaziesInfo(lazyFields);
|
| - return js.statement('''
|
| - (function(lazies) {
|
| - for (var i = 0; i < lazies.length; ) {
|
| - var fieldName = lazies[i++];
|
| - var getterName = lazies[i++];
|
| - if (#notMinified) {
|
| - var staticName = lazies[i++];
|
| - }
|
| - var lazyValue = lazies[i++];
|
| -
|
| - // We build the lazy-check here:
|
| - // lazyInitializer(fieldName, getterName, lazyValue, staticName);
|
| - // 'staticName' is used for error reporting in non-minified mode.
|
| - // 'lazyValue' must be a closure that constructs the initial value.
|
| - if (#notMinified) {
|
| - #lazy(fieldName, getterName, lazyValue, staticName);
|
| - } else {
|
| - #lazy(fieldName, getterName, lazyValue);
|
| - }
|
| - }
|
| - })(#laziesInfo)
|
| - ''', {'notMinified': !compiler.enableMinification,
|
| - 'laziesInfo': new jsAst.ArrayInitializer(laziesInfo),
|
| - 'lazy': js(lazyInitializerName)});
|
| - } else {
|
| - return js.comment("No lazy statics.");
|
| - }
|
| - }
|
| -
|
| - List<jsAst.Expression> buildLaziesInfo(List<VariableElement> lazies) {
|
| - List<jsAst.Expression> laziesInfo = <jsAst.Expression>[];
|
| - for (VariableElement element in Elements.sortedByPosition(lazies)) {
|
| - jsAst.Expression code = backend.generatedCode[element];
|
| - // The code is null if we ended up not needing the lazily
|
| - // initialized field after all because of constant folding
|
| - // before code generation.
|
| - if (code == null) continue;
|
| - laziesInfo.add(js.quoteName(namer.globalPropertyName(element)));
|
| - laziesInfo.add(js.quoteName(namer.lazyInitializerName(element)));
|
| - if (!compiler.enableMinification) {
|
| - laziesInfo.add(js.string(element.name));
|
| - }
|
| - laziesInfo.add(code);
|
| - }
|
| - return laziesInfo;
|
| - }
|
| -
|
| - // TODO(sra): Remove this unused function.
|
| - jsAst.Expression buildLazilyInitializedStaticField(
|
| - VariableElement element, {String isolateProperties}) {
|
| - jsAst.Expression code = backend.generatedCode[element];
|
| - // The code is null if we ended up not needing the lazily
|
| - // initialized field after all because of constant folding
|
| - // before code generation.
|
| - if (code == null) return null;
|
| - // The code only computes the initial value. We build the lazy-check
|
| - // here:
|
| - // lazyInitializer(fieldName, getterName, initial, name, prototype);
|
| - // The name is used for error reporting. The 'initial' must be a
|
| - // closure that constructs the initial value.
|
| - if (isolateProperties != null) {
|
| - // This is currently only used in incremental compilation to patch
|
| - // in new lazy values.
|
| - return js('#(#,#,#,#,#)',
|
| - [js(lazyInitializerName),
|
| - js.quoteName(namer.globalPropertyName(element)),
|
| - js.quoteName(namer.lazyInitializerName(element)),
|
| - code,
|
| - js.string(element.name),
|
| - isolateProperties]);
|
| - }
|
| -
|
| - if (compiler.enableMinification) {
|
| - return js('#(#,#,#)',
|
| - [js(lazyInitializerName),
|
| - js.quoteName(namer.globalPropertyName(element)),
|
| - js.quoteName(namer.lazyInitializerName(element)),
|
| - code]);
|
| - } else {
|
| - return js('#(#,#,#,#)',
|
| - [js(lazyInitializerName),
|
| - js.quoteName(namer.globalPropertyName(element)),
|
| - js.quoteName(namer.lazyInitializerName(element)),
|
| - code,
|
| - js.string(element.name)]);
|
| - }
|
| - }
|
| -
|
| - jsAst.Statement buildMetadata(Program program, OutputUnit outputUnit) {
|
| - List<jsAst.Statement> parts = <jsAst.Statement>[];
|
| -
|
| - jsAst.Expression types = program.metadataTypesForOutputUnit(outputUnit);
|
| -
|
| - if (outputUnit == compiler.deferredLoadTask.mainOutputUnit) {
|
| - jsAst.Expression metadataAccess =
|
| - generateEmbeddedGlobalAccess(embeddedNames.METADATA);
|
| - jsAst.Expression typesAccess =
|
| - generateEmbeddedGlobalAccess(embeddedNames.TYPES);
|
| -
|
| - parts..add(js.statement('# = #;', [metadataAccess, program.metadata]))
|
| - ..add(js.statement('# = #;', [typesAccess, types]));
|
| - } else if (types != null) {
|
| - parts.add(js.statement('var ${namer.deferredTypesName} = #;',
|
| - types));
|
| - }
|
| - return new jsAst.Block(parts);
|
| - }
|
| -
|
| - jsAst.Statement buildCompileTimeConstants(List<Constant> constants,
|
| - {bool isMainFragment}) {
|
| - assert(isMainFragment != null);
|
| -
|
| - if (constants.isEmpty) return js.comment("No constants in program.");
|
| - List<jsAst.Statement> parts = <jsAst.Statement>[];
|
| - if (compiler.hasIncrementalSupport && isMainFragment) {
|
| - parts = cachedEmittedConstantsAst;
|
| - }
|
| - for (Constant constant in constants) {
|
| - ConstantValue constantValue = constant.value;
|
| - if (compiler.hasIncrementalSupport && isMainFragment) {
|
| - if (cachedEmittedConstants.contains(constantValue)) continue;
|
| - cachedEmittedConstants.add(constantValue);
|
| - }
|
| - parts.add(buildConstantInitializer(constantValue));
|
| - }
|
| -
|
| - return new jsAst.Block(parts);
|
| - }
|
| -
|
| - jsAst.Statement buildConstantInitializer(ConstantValue constant) {
|
| - jsAst.Name name = namer.constantName(constant);
|
| - return js.statement('#.# = #',
|
| - [namer.globalObjectForConstant(constant), name,
|
| - constantInitializerExpression(constant)]);
|
| - }
|
| -
|
| - jsAst.Expression constantListGenerator(jsAst.Expression array) {
|
| - // TODO(floitsch): there is no harm in caching the template.
|
| - return js('${namer.isolateName}.#(#)', [makeConstListProperty, array]);
|
| - }
|
| -
|
| - jsAst.Statement buildMakeConstantList() {
|
| - if (task.outputContainsConstantList) {
|
| - return js.statement(r'''
|
| - // Functions are stored in the hidden class and not as properties in
|
| - // the object. We never actually look at the value, but only want
|
| - // to know if the property exists.
|
| - #.# = function (list) {
|
| - list.immutable$list = Array;
|
| - list.fixed$length = Array;
|
| - return list;
|
| - }''',
|
| - [namer.isolateName, makeConstListProperty]);
|
| - } else {
|
| - return js.comment("Output contains no constant list.");
|
| - }
|
| - }
|
| -
|
| - jsAst.Statement buildFunctionThatReturnsNull() {
|
| - return js.statement('#.# = function() {}',
|
| - [namer.isolateName,
|
| - backend.rti.getFunctionThatReturnsNullName]);
|
| - }
|
| -
|
| - jsAst.Expression generateFunctionThatReturnsNull() {
|
| - return js("#.#", [namer.isolateName,
|
| - backend.rti.getFunctionThatReturnsNullName]);
|
| - }
|
| -
|
| - buildMain(jsAst.Statement invokeMain) {
|
| - if (compiler.isMockCompilation) return js.comment("Mock compilation");
|
| -
|
| - List<jsAst.Statement> parts = <jsAst.Statement>[];
|
| -
|
| - if (NativeGenerator.needsIsolateAffinityTagInitialization(backend)) {
|
| - parts.add(
|
| - NativeGenerator.generateIsolateAffinityTagInitialization(
|
| - backend,
|
| - generateEmbeddedGlobalAccess,
|
| - js("convertToFastObject", [])));
|
| - }
|
| -
|
| - parts..add(js.comment('BEGIN invoke [main].'))
|
| - ..add(invokeMain)
|
| - ..add(js.comment('END invoke [main].'));
|
| -
|
| - return new jsAst.Block(parts);
|
| - }
|
| -
|
| - jsAst.Statement buildInitFunction() {
|
| - jsAst.Expression allClassesAccess =
|
| - generateEmbeddedGlobalAccess(embeddedNames.ALL_CLASSES);
|
| - jsAst.Expression getTypeFromNameAccess =
|
| - generateEmbeddedGlobalAccess(embeddedNames.GET_TYPE_FROM_NAME);
|
| - jsAst.Expression interceptorsByTagAccess =
|
| - generateEmbeddedGlobalAccess(embeddedNames.INTERCEPTORS_BY_TAG);
|
| - jsAst.Expression leafTagsAccess =
|
| - generateEmbeddedGlobalAccess(embeddedNames.LEAF_TAGS);
|
| - jsAst.Expression finishedClassesAccess =
|
| - generateEmbeddedGlobalAccess(embeddedNames.FINISHED_CLASSES);
|
| - jsAst.Expression cyclicThrow =
|
| - staticFunctionAccess(backend.getCyclicThrowHelper());
|
| - jsAst.Expression laziesAccess =
|
| - generateEmbeddedGlobalAccess(embeddedNames.LAZIES);
|
| -
|
| - return js.statement('''
|
| - function init() {
|
| - $isolatePropertiesName = Object.create(null);
|
| - #allClasses = map();
|
| - #getTypeFromName = function(name) {return #allClasses[name];};
|
| - #interceptorsByTag = map();
|
| - #leafTags = map();
|
| - #finishedClasses = map();
|
| -
|
| - if (#needsLazyInitializer) {
|
| - // [staticName] is only provided in non-minified mode. If missing, we
|
| - // fall back to [fieldName]. Likewise, [prototype] is optional and
|
| - // defaults to the isolateProperties object.
|
| - $lazyInitializerName = function (fieldName, getterName, lazyValue,
|
| - staticName, prototype) {
|
| - if (!#lazies) #lazies = Object.create(null);
|
| - #lazies[fieldName] = getterName;
|
| -
|
| - // 'prototype' will be undefined except if we are doing an update
|
| - // during incremental compilation. In this case we put the lazy
|
| - // field directly on the isolate instead of the isolateProperties.
|
| - prototype = prototype || $isolatePropertiesName;
|
| - var sentinelUndefined = {};
|
| - var sentinelInProgress = {};
|
| - prototype[fieldName] = sentinelUndefined;
|
| -
|
| - prototype[getterName] = function () {
|
| - var result = this[fieldName];
|
| - try {
|
| - if (result === sentinelUndefined) {
|
| - this[fieldName] = sentinelInProgress;
|
| -
|
| - try {
|
| - result = this[fieldName] = lazyValue();
|
| - } finally {
|
| - // Use try-finally, not try-catch/throw as it destroys the
|
| - // stack trace.
|
| - if (result === sentinelUndefined)
|
| - this[fieldName] = null;
|
| - }
|
| - } else {
|
| - if (result === sentinelInProgress)
|
| - // In minified mode, static name is not provided, so fall
|
| - // back to the minified fieldName.
|
| - #cyclicThrow(staticName || fieldName);
|
| - }
|
| -
|
| - return result;
|
| - } finally {
|
| - this[getterName] = function() { return this[fieldName]; };
|
| - }
|
| - }
|
| - }
|
| - }
|
| -
|
| - // We replace the old Isolate function with a new one that initializes
|
| - // all its fields with the initial (and often final) value of all
|
| - // globals.
|
| - //
|
| - // We also copy over old values like the prototype, and the
|
| - // isolateProperties themselves.
|
| - $finishIsolateConstructorName = function (oldIsolate) {
|
| - var isolateProperties = oldIsolate.#isolatePropertiesName;
|
| - function Isolate() {
|
| -
|
| - var staticNames = Object.keys(isolateProperties);
|
| - for (var i = 0; i < staticNames.length; i++) {
|
| - var staticName = staticNames[i];
|
| - this[staticName] = isolateProperties[staticName];
|
| - }
|
| -
|
| - // Reset lazy initializers to null.
|
| - // When forcing the object to fast mode (below) v8 will consider
|
| - // functions as part the object's map. Since we will change them
|
| - // (after the first call to the getter), we would have a map
|
| - // transition.
|
| - var lazies = init.lazies;
|
| - var lazyInitializers = lazies ? Object.keys(lazies) : [];
|
| - for (var i = 0; i < lazyInitializers.length; i++) {
|
| - this[lazies[lazyInitializers[i]]] = null;
|
| - }
|
| -
|
| - // Use the newly created object as prototype. In Chrome,
|
| - // this creates a hidden class for the object and makes
|
| - // sure it is fast to access.
|
| - function ForceEfficientMap() {}
|
| - ForceEfficientMap.prototype = this;
|
| - new ForceEfficientMap();
|
| -
|
| - // Now, after being a fast map we can set the lazies again.
|
| - for (var i = 0; i < lazyInitializers.length; i++) {
|
| - var lazyInitName = lazies[lazyInitializers[i]];
|
| - this[lazyInitName] = isolateProperties[lazyInitName];
|
| - }
|
| - }
|
| - Isolate.prototype = oldIsolate.prototype;
|
| - Isolate.prototype.constructor = Isolate;
|
| - Isolate.#isolatePropertiesName = isolateProperties;
|
| - if (#outputContainsConstantList) {
|
| - Isolate.#makeConstListProperty = oldIsolate.#makeConstListProperty;
|
| - }
|
| - Isolate.#functionThatReturnsNullProperty =
|
| - oldIsolate.#functionThatReturnsNullProperty;
|
| - if (#hasIncrementalSupport) {
|
| - Isolate.#lazyInitializerProperty =
|
| - oldIsolate.#lazyInitializerProperty;
|
| - }
|
| - return Isolate;
|
| - }
|
| -
|
| - }''', {'allClasses': allClassesAccess,
|
| - 'getTypeFromName': getTypeFromNameAccess,
|
| - 'interceptorsByTag': interceptorsByTagAccess,
|
| - 'leafTags': leafTagsAccess,
|
| - 'finishedClasses': finishedClassesAccess,
|
| - 'needsLazyInitializer': needsLazyInitializer,
|
| - 'lazies': laziesAccess, 'cyclicThrow': cyclicThrow,
|
| - 'isolatePropertiesName': namer.isolatePropertiesName,
|
| - 'outputContainsConstantList': task.outputContainsConstantList,
|
| - 'makeConstListProperty': makeConstListProperty,
|
| - 'functionThatReturnsNullProperty':
|
| - backend.rti.getFunctionThatReturnsNullName,
|
| - 'hasIncrementalSupport': compiler.hasIncrementalSupport,
|
| - 'lazyInitializerProperty': lazyInitializerProperty,});
|
| - }
|
| -
|
| - jsAst.Statement buildConvertToFastObjectFunction() {
|
| - List<jsAst.Statement> debugCode = <jsAst.Statement>[];
|
| - if (DEBUG_FAST_OBJECTS) {
|
| - debugCode.add(js.statement(r'''
|
| - // The following only works on V8 when run with option
|
| - // "--allow-natives-syntax". We use'new Function' because the
|
| - // miniparser does not understand V8 native syntax.
|
| - if (typeof print === "function") {
|
| - var HasFastProperties =
|
| - new Function("a", "return %HasFastProperties(a)");
|
| - print("Size of global object: "
|
| - + String(Object.getOwnPropertyNames(properties).length)
|
| - + ", fast properties " + HasFastProperties(properties));
|
| - }'''));
|
| - }
|
| -
|
| - return js.statement(r'''
|
| - function convertToFastObject(properties) {
|
| - // Create an instance that uses 'properties' as prototype. This should
|
| - // make 'properties' a fast object.
|
| - function MyClass() {};
|
| - MyClass.prototype = properties;
|
| - new MyClass();
|
| - #;
|
| - return properties;
|
| - }''', [debugCode]);
|
| - }
|
| -
|
| - jsAst.Statement buildConvertToSlowObjectFunction() {
|
| - return js.statement(r'''
|
| - function convertToSlowObject(properties) {
|
| - // Add and remove a property to make the object transition into hashmap
|
| - // mode.
|
| - properties.__MAGIC_SLOW_PROPERTY = 1;
|
| - delete properties.__MAGIC_SLOW_PROPERTY;
|
| - return properties;
|
| - }''');
|
| - }
|
| -
|
| - jsAst.Statement buildSupportsDirectProtoAccess() {
|
| - jsAst.Statement supportsDirectProtoAccess;
|
| -
|
| - if (compiler.hasIncrementalSupport) {
|
| - supportsDirectProtoAccess = js.statement(r'''
|
| - var supportsDirectProtoAccess = false;
|
| - ''');
|
| - } else {
|
| - supportsDirectProtoAccess = js.statement(r'''
|
| - var supportsDirectProtoAccess = (function () {
|
| - var cls = function () {};
|
| - cls.prototype = {'p': {}};
|
| - var object = new cls();
|
| - return object.__proto__ &&
|
| - object.__proto__.p === cls.prototype.p;
|
| - })();
|
| - ''');
|
| - }
|
| -
|
| - return supportsDirectProtoAccess;
|
| - }
|
| -
|
| - jsAst.Expression generateLibraryDescriptor(LibraryElement library,
|
| - Fragment fragment) {
|
| - var uri = "";
|
| - if (!compiler.enableMinification || backend.mustPreserveUris) {
|
| - uri = library.canonicalUri;
|
| - if (uri.scheme == 'file' && compiler.outputUri != null) {
|
| - uri = relativize(compiler.outputUri, library.canonicalUri, false);
|
| - }
|
| - }
|
| -
|
| - String libraryName =
|
| - (!compiler.enableMinification || backend.mustRetainLibraryNames) ?
|
| - library.getLibraryName() :
|
| - "";
|
| -
|
| - jsAst.Fun metadata = task.metadataCollector.buildMetadataFunction(library);
|
| -
|
| - ClassBuilder descriptor = elementDescriptors[fragment][library];
|
| -
|
| - jsAst.ObjectInitializer initializer;
|
| - if (descriptor == null) {
|
| - // Nothing of the library was emitted.
|
| - // TODO(floitsch): this should not happen. We currently have an example
|
| - // with language/prefix6_negative_test.dart where we have an instance
|
| - // method without its corresponding class.
|
| - initializer = new jsAst.ObjectInitializer([]);
|
| - } else {
|
| - initializer = descriptor.toObjectInitializer();
|
| - }
|
| -
|
| - compiler.dumpInfoTask.registerElementAst(library, metadata);
|
| - compiler.dumpInfoTask.registerElementAst(library, initializer);
|
| -
|
| - List<jsAst.Expression> parts = <jsAst.Expression>[];
|
| - parts..add(js.string(libraryName))
|
| - ..add(js.string(uri.toString()))
|
| - ..add(metadata == null ? new jsAst.ArrayHole() : metadata)
|
| - ..add(js('#', namer.globalObjectFor(library)))
|
| - ..add(initializer);
|
| - if (library == compiler.mainApp) {
|
| - parts.add(js.number(1));
|
| - }
|
| -
|
| - return new jsAst.ArrayInitializer(parts);
|
| - }
|
| -
|
| - void assemblePrecompiledConstructor(OutputUnit outputUnit,
|
| - jsAst.Name constructorName,
|
| - jsAst.Expression constructorAst,
|
| - List<jsAst.Name> fields) {
|
| - cspPrecompiledFunctionFor(outputUnit).add(
|
| - new jsAst.FunctionDeclaration(constructorName, constructorAst));
|
| -
|
| - String fieldNamesProperty = FIELD_NAMES_PROPERTY_NAME;
|
| - bool hasIsolateSupport = compiler.hasIsolateSupport;
|
| - jsAst.Node fieldNamesArray;
|
| - if (hasIsolateSupport) {
|
| - fieldNamesArray =
|
| - new jsAst.ArrayInitializer(fields.map(js.quoteName).toList());
|
| - } else {
|
| - fieldNamesArray = new jsAst.LiteralNull();
|
| - }
|
| -
|
| - cspPrecompiledFunctionFor(outputUnit).add(js.statement(r'''
|
| - {
|
| - #constructorName.#typeNameProperty = #constructorNameString;
|
| - // IE does not have a name property.
|
| - if (!("name" in #constructorName))
|
| - #constructorName.name = #constructorNameString;
|
| - $desc = $collectedClasses$.#constructorName[1];
|
| - #constructorName.prototype = $desc;
|
| - ''' /* next string is not a raw string */ '''
|
| - if (#hasIsolateSupport) {
|
| - #constructorName.$fieldNamesProperty = #fieldNamesArray;
|
| - }
|
| - }''',
|
| - {"constructorName": constructorName,
|
| - "typeNameProperty": typeNameProperty,
|
| - "constructorNameString": js.quoteName(constructorName),
|
| - "hasIsolateSupport": hasIsolateSupport,
|
| - "fieldNamesArray": fieldNamesArray}));
|
| -
|
| - cspPrecompiledConstructorNamesFor(outputUnit).add(js('#', constructorName));
|
| - }
|
| -
|
| - void assembleTypedefs(Program program) {
|
| - Fragment mainFragment = program.mainFragment;
|
| - OutputUnit mainOutputUnit = mainFragment.outputUnit;
|
| -
|
| - // Emit all required typedef declarations into the main output unit.
|
| - // TODO(karlklose): unify required classes and typedefs to declarations
|
| - // and have builders for each kind.
|
| - for (TypedefElement typedef in typedefsNeededForReflection) {
|
| - LibraryElement library = typedef.library;
|
| - // TODO(karlklose): add a TypedefBuilder and move this code there.
|
| - DartType type = typedef.alias;
|
| - // TODO(zarah): reify type variables once reflection on type arguments of
|
| - // typedefs is supported.
|
| - jsAst.Expression typeIndex =
|
| - task.metadataCollector.reifyType(type, ignoreTypeVariables: true);
|
| - ClassBuilder builder = new ClassBuilder.forStatics(typedef, namer);
|
| - builder.addPropertyByName(embeddedNames.TYPEDEF_TYPE_PROPERTY_NAME,
|
| - typeIndex);
|
| - builder.addPropertyByName(embeddedNames.TYPEDEF_PREDICATE_PROPERTY_NAME,
|
| - js.boolean(true));
|
| -
|
| - // We can be pretty sure that the objectClass is initialized, since
|
| - // typedefs are only emitted with reflection, which requires lots of
|
| - // classes.
|
| - assert(compiler.objectClass != null);
|
| - builder.superName = namer.className(compiler.objectClass);
|
| - jsAst.Node declaration = builder.toObjectInitializer();
|
| - jsAst.Name mangledName = namer.globalPropertyName(typedef);
|
| - String reflectionName = getReflectionName(typedef, mangledName);
|
| - getElementDescriptor(library, mainFragment)
|
| - ..addProperty(mangledName, declaration)
|
| - ..addPropertyByName("+$reflectionName", js.string(''));
|
| - // Also emit a trivial constructor for CSP mode.
|
| - jsAst.Name constructorName = mangledName;
|
| - jsAst.Expression constructorAst = js('function() {}');
|
| - List<jsAst.Name> fieldNames = [];
|
| - assemblePrecompiledConstructor(mainOutputUnit,
|
| - constructorName,
|
| - constructorAst,
|
| - fieldNames);
|
| - }
|
| - }
|
| -
|
| - jsAst.Statement buildGlobalObjectSetup(bool isProgramSplit) {
|
| - List<jsAst.Statement> parts = <jsAst.Statement>[];
|
| -
|
| - parts.add(js.comment("""
|
| - // The global objects start as so-called "slow objects". For V8, this
|
| - // means that it won't try to make map transitions as we add properties
|
| - // to these objects. Later on, we attempt to turn these objects into
|
| - // fast objects by calling "convertToFastObject" (see
|
| - // [emitConvertToFastObjectFunction]).
|
| - """));
|
| -
|
| - for (String globalObject in Namer.reservedGlobalObjectNames) {
|
| - if (isProgramSplit) {
|
| - String template =
|
| - "var #globalObject = #globalsHolder.#globalObject = map();";
|
| - parts.add(js.statement(template, {"globalObject": globalObject,
|
| - "globalsHolder": globalsHolder}));
|
| - } else {
|
| - parts.add(js.statement("var #globalObject = map();",
|
| - {"globalObject": globalObject}));
|
| - }
|
| -
|
| - }
|
| -
|
| - return new jsAst.Block(parts);
|
| - }
|
| -
|
| - jsAst.Statement buildConvertGlobalObjectToFastObjects() {
|
| - List<jsAst.Statement> parts = <jsAst.Statement>[];
|
| -
|
| - for (String globalObject in Namer.reservedGlobalObjectNames) {
|
| - parts.add(js.statement(
|
| - '#globalObject = convertToFastObject(#globalObject);',
|
| - {"globalObject": globalObject}));
|
| - }
|
| -
|
| - return new jsAst.Block(parts);
|
| - }
|
| -
|
| - jsAst.Statement buildDebugFastObjectCode() {
|
| - List<jsAst.Statement> parts = <jsAst.Statement>[];
|
| -
|
| - if (DEBUG_FAST_OBJECTS) {
|
| - parts.add(js.statement(r'''
|
| - // The following only works on V8 when run with option
|
| - // "--allow-natives-syntax". We use'new Function' because the
|
| - // miniparser does not understand V8 native syntax.
|
| - if (typeof print === "function") {
|
| - var HasFastProperties =
|
| - new Function("a", "return %HasFastProperties(a)");
|
| - print("Size of global helper object: "
|
| - + String(Object.getOwnPropertyNames(H).length)
|
| - + ", fast properties " + HasFastProperties(H));
|
| - print("Size of global platform object: "
|
| - + String(Object.getOwnPropertyNames(P).length)
|
| - + ", fast properties " + HasFastProperties(P));
|
| - print("Size of global dart:html object: "
|
| - + String(Object.getOwnPropertyNames(W).length)
|
| - + ", fast properties " + HasFastProperties(W));
|
| - print("Size of isolate properties object: "
|
| - + String(Object.getOwnPropertyNames($).length)
|
| - + ", fast properties " + HasFastProperties($));
|
| - print("Size of constant object: "
|
| - + String(Object.getOwnPropertyNames(C).length)
|
| - + ", fast properties " + HasFastProperties(C));
|
| - var names = Object.getOwnPropertyNames($);
|
| - for (var i = 0; i < names.length; i++) {
|
| - print("$." + names[i]);
|
| - }
|
| - }
|
| - '''));
|
| -
|
| - for (String object in Namer.userGlobalObjects) {
|
| - parts.add(js.statement('''
|
| - if (typeof print === "function") {
|
| - print("Size of " + #objectString + ": "
|
| - + String(Object.getOwnPropertyNames(#object).length)
|
| - + ", fast properties " + HasFastProperties(#object));
|
| - }
|
| - ''', {"object": object, "objectString": js.string(object)}));
|
| - }
|
| - }
|
| -
|
| - return new jsAst.Block(parts);
|
| - }
|
| -
|
| - jsAst.Statement buildMangledNames() {
|
| - List<jsAst.Statement> parts = <jsAst.Statement>[];
|
| -
|
| - if (!mangledFieldNames.isEmpty) {
|
| - List<jsAst.Name> keys = mangledFieldNames.keys.toList()..sort();
|
| - var properties = [];
|
| - for (jsAst.Name key in keys) {
|
| - var value = js.string(mangledFieldNames[key]);
|
| - properties.add(new jsAst.Property(key, value));
|
| - }
|
| -
|
| - jsAst.Expression mangledNamesAccess =
|
| - generateEmbeddedGlobalAccess(embeddedNames.MANGLED_NAMES);
|
| - var map = new jsAst.ObjectInitializer(properties);
|
| - parts.add(js.statement('# = #', [mangledNamesAccess, map]));
|
| - }
|
| -
|
| - if (!mangledGlobalFieldNames.isEmpty) {
|
| - List<jsAst.Name> keys = mangledGlobalFieldNames.keys.toList()
|
| - ..sort();
|
| - List<jsAst.Property> properties = <jsAst.Property>[];
|
| - for (jsAst.Name key in keys) {
|
| - jsAst.Literal value = js.string(mangledGlobalFieldNames[key]);
|
| - properties.add(new jsAst.Property(js.quoteName(key), value));
|
| - }
|
| - jsAst.Expression mangledGlobalNamesAccess =
|
| - generateEmbeddedGlobalAccess(embeddedNames.MANGLED_GLOBAL_NAMES);
|
| - jsAst.ObjectInitializer map = new jsAst.ObjectInitializer(properties);
|
| - parts.add(js.statement('# = #', [mangledGlobalNamesAccess, map]));
|
| - }
|
| -
|
| - return new jsAst.Block(parts);
|
| - }
|
| -
|
| - void checkEverythingEmitted(Iterable<Element> elements) {
|
| - List<Element> pendingStatics;
|
| - if (!compiler.hasIncrementalSupport) {
|
| - pendingStatics =
|
| - Elements.sortedByPosition(elements.where((e) => !e.isLibrary));
|
| -
|
| - pendingStatics.forEach((element) =>
|
| - compiler.reportInfo(
|
| - element, MessageKind.GENERIC, {'text': 'Pending statics.'}));
|
| - }
|
| -
|
| - if (pendingStatics != null && !pendingStatics.isEmpty) {
|
| - compiler.internalError(pendingStatics.first,
|
| - 'Pending statics (see above).');
|
| - }
|
| - }
|
| -
|
| - void assembleLibrary(Library library, Fragment fragment) {
|
| - LibraryElement libraryElement = library.element;
|
| -
|
| - assembleStaticFunctions(library.statics, fragment);
|
| -
|
| - ClassBuilder libraryBuilder =
|
| - getElementDescriptor(libraryElement, fragment);
|
| - for (Class cls in library.classes) {
|
| - assembleClass(cls, libraryBuilder, fragment);
|
| - }
|
| -
|
| - classEmitter.emitFields(library, libraryBuilder, emitStatics: true);
|
| - }
|
| -
|
| - void assembleProgram(Program program) {
|
| - for (Fragment fragment in program.fragments) {
|
| - for (Library library in fragment.libraries) {
|
| - assembleLibrary(library, fragment);
|
| - }
|
| - }
|
| - assembleTypedefs(program);
|
| - }
|
| -
|
| - jsAst.Program buildOutputAstForMain(Program program,
|
| - Map<OutputUnit, _DeferredOutputUnitHash> deferredLoadHashes) {
|
| - MainFragment mainFragment = program.mainFragment;
|
| - OutputUnit mainOutputUnit = mainFragment.outputUnit;
|
| - bool isProgramSplit = program.isSplit;
|
| -
|
| - List<jsAst.Statement> statements = <jsAst.Statement>[];
|
| -
|
| - statements..add(buildGeneratedBy())
|
| - ..add(js.comment(HOOKS_API_USAGE));
|
| -
|
| - if (isProgramSplit) {
|
| - /// For deferred loading we communicate the initializers via this global
|
| - /// variable. The deferred hunks will add their initialization to this.
|
| - /// The semicolon is important in minified mode, without it the
|
| - /// following parenthesis looks like a call to the object literal.
|
| - statements.add(
|
| - js.statement('self.#deferredInitializers = '
|
| - 'self.#deferredInitializers || Object.create(null);',
|
| - {'deferredInitializers': deferredInitializers}));
|
| - }
|
| -
|
| - // Collect the AST for the decriptors
|
| - Map<Element, ClassBuilder> descriptors = elementDescriptors[mainFragment];
|
| - if (descriptors == null) descriptors = const {};
|
| -
|
| - checkEverythingEmitted(descriptors.keys);
|
| -
|
| - Iterable<LibraryElement> libraries =
|
| - task.outputLibraryLists[mainOutputUnit];
|
| - if (libraries == null) libraries = <LibraryElement>[];
|
| -
|
| - List<jsAst.Expression> parts = <jsAst.Expression>[];
|
| - for (LibraryElement library in Elements.sortedByPosition(libraries)) {
|
| - parts.add(generateLibraryDescriptor(library, mainFragment));
|
| - descriptors.remove(library);
|
| - }
|
| -
|
| - if (descriptors.isNotEmpty) {
|
| - List<Element> remainingLibraries = descriptors.keys
|
| - .where((Element e) => e is LibraryElement)
|
| - .toList();
|
| -
|
| - // The remaining descriptors are only accessible through reflection.
|
| - // The program builder does not collect libraries that only
|
| - // contain typedefs that are used for reflection.
|
| - for (LibraryElement element in remainingLibraries) {
|
| - assert(element is LibraryElement || compiler.hasIncrementalSupport);
|
| - if (element is LibraryElement) {
|
| - parts.add(generateLibraryDescriptor(element, mainFragment));
|
| - descriptors.remove(element);
|
| - }
|
| - }
|
| - }
|
| - jsAst.ArrayInitializer descriptorsAst = new jsAst.ArrayInitializer(parts);
|
| -
|
| - // Using a named function here produces easier to read stack traces in
|
| - // Chrome/V8.
|
| - statements.add(js.statement("""
|
| - (function() {
|
| - // No renaming in the top-level function to save the locals for the
|
| - // nested context where they will be used more. We have to put the
|
| - // comment into a hole as the parser strips out comments right away.
|
| - #disableVariableRenaming;
|
| - #supportsDirectProtoAccess;
|
| -
|
| - if (#hasIncrementalSupport) {
|
| - #helper = #helper || Object.create(null);
|
| - #helper.patch = function(a) { eval(a)};
|
| - #helper.schemaChange = #schemaChange;
|
| - #helper.addMethod = #addMethod;
|
| - #helper.extractStubs =
|
| - function(array, name, isStatic, originalDescriptor) {
|
| - var descriptor = Object.create(null);
|
| - this.addStubs(descriptor, array, name, isStatic, []);
|
| - return descriptor;
|
| - };
|
| - }
|
| -
|
| - if (#isProgramSplit) {
|
| - /// We collect all the global state, so it can be passed to the
|
| - /// initializer of deferred files.
|
| - var #globalsHolder = Object.create(null)
|
| - }
|
| -
|
| - // [map] returns an object 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.
|
| - // It requires fewer characters to declare a variable as a parameter than
|
| - // with `var`.
|
| - function map(x) {
|
| - x = Object.create(null);
|
| - x.x = 0;
|
| - delete x.x;
|
| - return x;
|
| - }
|
| -
|
| - #globalObjectSetup;
|
| -
|
| - function #isolateName() {}
|
| -
|
| - if (#isProgramSplit) {
|
| - #globalsHolder.#isolateName = #isolateName;
|
| - #globalsHolder.#initName = #initName;
|
| - #globalsHolder.#setupProgramName = #setupProgramName;
|
| - }
|
| -
|
| - init();
|
| -
|
| - #mangledNames;
|
| -
|
| - #cspPrecompiledFunctions;
|
| -
|
| - #setupProgram;
|
| -
|
| - #functionThatReturnsNull;
|
| -
|
| - // The argument to reflectionDataParser is assigned to a temporary 'dart'
|
| - // so that 'dart.' will appear as the prefix to dart methods in stack
|
| - // traces and profile entries.
|
| - var dart = #descriptors;
|
| -
|
| - #setupProgramName(dart, 0);
|
| -
|
| - #getInterceptorMethods;
|
| - #oneShotInterceptors;
|
| -
|
| - #makeConstantList;
|
| -
|
| - // We abuse the short name used for the isolate here to store
|
| - // the isolate properties. This is safe as long as the real isolate
|
| - // object does not exist yet.
|
| - var ${namer.currentIsolate} = #isolatePropertiesName;
|
| -
|
| - // Constants in checked mode call into RTI code to set type information
|
| - // which may need getInterceptor (and one-shot interceptor) methods, so
|
| - // we have to make sure that [emitGetInterceptorMethods] and
|
| - // [emitOneShotInterceptors] have been called.
|
| - #compileTimeConstants;
|
| -
|
| - // Static field initializations require the classes and compile-time
|
| - // constants to be set up.
|
| - #staticNonFinalInitializers;
|
| -
|
| - ${namer.currentIsolate} = null;
|
| -
|
| - #deferredBoilerPlate;
|
| -
|
| - #typeToInterceptorMap;
|
| -
|
| - #lazyStaticFields;
|
| -
|
| - #isolateName = $finishIsolateConstructorName(#isolateName);
|
| -
|
| - ${namer.currentIsolate} = new #isolateName();
|
| -
|
| - #metadata;
|
| -
|
| - #convertToFastObject;
|
| - #convertToSlowObject;
|
| -
|
| - #convertGlobalObjectsToFastObjects;
|
| - #debugFastObjects;
|
| -
|
| - #init;
|
| -
|
| - #main;
|
| - })();
|
| - """, {
|
| - "disableVariableRenaming": js.comment("/* ::norenaming:: */"),
|
| - "hasIncrementalSupport": compiler.hasIncrementalSupport,
|
| - "helper": js('this.#', [namer.incrementalHelperName]),
|
| - "schemaChange": buildSchemaChangeFunction(),
|
| - "addMethod": buildIncrementalAddMethod(),
|
| - "isProgramSplit": isProgramSplit,
|
| - "supportsDirectProtoAccess": buildSupportsDirectProtoAccess(),
|
| - "globalsHolder": globalsHolder,
|
| - "globalObjectSetup": buildGlobalObjectSetup(isProgramSplit),
|
| - "isolateName": namer.isolateName,
|
| - "isolatePropertiesName": js(isolatePropertiesName),
|
| - "initName": initName,
|
| - "functionThatReturnsNull": buildFunctionThatReturnsNull(),
|
| - "mangledNames": buildMangledNames(),
|
| - "setupProgram": buildSetupProgram(program, compiler, backend, namer, this),
|
| - "setupProgramName": setupProgramName,
|
| - "descriptors": descriptorsAst,
|
| - "cspPrecompiledFunctions": buildCspPrecompiledFunctionFor(mainOutputUnit),
|
| - "getInterceptorMethods": interceptorEmitter.buildGetInterceptorMethods(),
|
| - "oneShotInterceptors": interceptorEmitter.buildOneShotInterceptors(),
|
| - "makeConstantList": buildMakeConstantList(),
|
| - "compileTimeConstants": buildCompileTimeConstants(mainFragment.constants,
|
| - isMainFragment: true),
|
| - "deferredBoilerPlate": buildDeferredBoilerPlate(deferredLoadHashes),
|
| - "staticNonFinalInitializers": buildStaticNonFinalFieldInitializations(
|
| - mainOutputUnit),
|
| - "typeToInterceptorMap":
|
| - interceptorEmitter.buildTypeToInterceptorMap(program),
|
| - "lazyStaticFields": buildLazilyInitializedStaticFields(),
|
| - "metadata": buildMetadata(program, mainOutputUnit),
|
| - "convertToFastObject": buildConvertToFastObjectFunction(),
|
| - "convertToSlowObject": buildConvertToSlowObjectFunction(),
|
| - "convertGlobalObjectsToFastObjects":
|
| - buildConvertGlobalObjectToFastObjects(),
|
| - "debugFastObjects": buildDebugFastObjectCode(),
|
| - "init": buildInitFunction(),
|
| - "main": buildMain(mainFragment.invokeMain)
|
| - }));
|
| -
|
| - return new jsAst.Program(statements);
|
| - }
|
| -
|
| - void emitMainOutputUnit(OutputUnit mainOutputUnit, jsAst.Program program) {
|
| - LineColumnCollector lineColumnCollector;
|
| - List<CodeOutputListener> codeOutputListeners;
|
| - if (generateSourceMap) {
|
| - lineColumnCollector = new LineColumnCollector();
|
| - codeOutputListeners = <CodeOutputListener>[lineColumnCollector];
|
| - }
|
| -
|
| - CodeOutput mainOutput =
|
| - new StreamCodeOutput(compiler.outputProvider('', 'js'),
|
| - codeOutputListeners);
|
| - outputBuffers[mainOutputUnit] = mainOutput;
|
| -
|
| -
|
| - mainOutput.addBuffer(jsAst.prettyPrint(program,
|
| - compiler,
|
| - monitor: compiler.dumpInfoTask));
|
| -
|
| - if (compiler.deferredMapUri != null) {
|
| - outputDeferredMap();
|
| - }
|
| -
|
| - if (generateSourceMap) {
|
| - mainOutput.add(
|
| - generateSourceMapTag(compiler.sourceMapUri, compiler.outputUri));
|
| - }
|
| -
|
| - mainOutput.close();
|
| -
|
| - if (generateSourceMap) {
|
| - outputSourceMap(mainOutput, lineColumnCollector, '',
|
| - compiler.sourceMapUri, compiler.outputUri);
|
| - }
|
| - }
|
| -
|
| - /// Used by incremental compilation to patch up the prototype of
|
| - /// [oldConstructor] for use as prototype of [newConstructor].
|
| - jsAst.Fun buildSchemaChangeFunction() {
|
| - if (!compiler.hasIncrementalSupport) return null;
|
| - return js('''
|
| -function(newConstructor, oldConstructor, superclass) {
|
| - // Invariant: newConstructor.prototype has no interesting properties besides
|
| - // generated accessors. These are copied to oldPrototype which will be
|
| - // updated by other incremental changes.
|
| - if (superclass != null) {
|
| - this.inheritFrom(newConstructor, superclass);
|
| - }
|
| - var oldPrototype = oldConstructor.prototype;
|
| - var newPrototype = newConstructor.prototype;
|
| - var hasOwnProperty = Object.prototype.hasOwnProperty;
|
| - for (var property in newPrototype) {
|
| - if (hasOwnProperty.call(newPrototype, property)) {
|
| - // Copy generated accessors.
|
| - oldPrototype[property] = newPrototype[property];
|
| - }
|
| - }
|
| - oldPrototype.__proto__ = newConstructor.prototype.__proto__;
|
| - oldPrototype.constructor = newConstructor;
|
| - newConstructor.prototype = oldPrototype;
|
| - return newConstructor;
|
| -}''');
|
| - }
|
| -
|
| - /// Used by incremental compilation to patch up an object ([holder]) with a
|
| - /// new (or updated) method. [arrayOrFunction] is either the new method, or
|
| - /// an array containing the method (see
|
| - /// [ContainerBuilder.addMemberMethodFromInfo]). [name] is the name of the
|
| - /// new method. [isStatic] tells if method is static (or
|
| - /// top-level). [globalFunctionsAccess] is a reference to
|
| - /// [embeddedNames.GLOBAL_FUNCTIONS].
|
| - jsAst.Fun buildIncrementalAddMethod() {
|
| - if (!compiler.hasIncrementalSupport) return null;
|
| - return js(r"""
|
| -function(originalDescriptor, name, holder, isStatic, globalFunctionsAccess) {
|
| - var arrayOrFunction = originalDescriptor[name];
|
| - var method;
|
| - if (arrayOrFunction.constructor === Array) {
|
| - var existing = holder[name];
|
| - var array = arrayOrFunction;
|
| -
|
| - // Each method may have a number of stubs associated. For example, if an
|
| - // instance method supports multiple arguments, a stub for each matching
|
| - // selector. There is also a getter stub for tear-off getters. For example,
|
| - // an instance method foo([a]) may have the following stubs: foo$0, foo$1,
|
| - // and get$foo (here exemplified using unminified names).
|
| - // [extractStubs] returns a JavaScript object whose own properties
|
| - // corresponds to the stubs.
|
| - var descriptor =
|
| - this.extractStubs(array, name, isStatic, originalDescriptor);
|
| - method = descriptor[name];
|
| -
|
| - // Iterate through the properties of descriptor and copy the stubs to the
|
| - // existing holder (for instance methods, a prototype).
|
| - for (var property in descriptor) {
|
| - if (!Object.prototype.hasOwnProperty.call(descriptor, property)) continue;
|
| - var stub = descriptor[property];
|
| - var existingStub = holder[property];
|
| - if (stub === method || !existingStub || !stub.$getterStub) {
|
| - // Not replacing an existing getter stub.
|
| - holder[property] = stub;
|
| - continue;
|
| - }
|
| - if (!stub.$getterStub) {
|
| - var error = new Error('Unexpected stub.');
|
| - error.stub = stub;
|
| - throw error;
|
| - }
|
| -
|
| - // Existing getter stubs need special treatment as they may already have
|
| - // been called and produced a closure.
|
| - this.pendingStubs = this.pendingStubs || [];
|
| - // It isn't safe to invoke the stub yet.
|
| - this.pendingStubs.push((function(holder, stub, existingStub, existing,
|
| - method) {
|
| - return function() {
|
| - var receiver = isStatic ? holder : new holder.constructor();
|
| - // Invoke the existing stub to obtain the tear-off closure.
|
| - existingStub = existingStub.call(receiver);
|
| - // Invoke the new stub to create a tear-off closure we can use as a
|
| - // prototype.
|
| - stub = stub.call(receiver);
|
| -
|
| - // Copy the properties from the new tear-off's prototype to the
|
| - // prototype of the existing tear-off.
|
| - var newProto = stub.constructor.prototype;
|
| - var existingProto = existingStub.constructor.prototype;
|
| - for (var stubProperty in newProto) {
|
| - if (!Object.prototype.hasOwnProperty.call(newProto, stubProperty))
|
| - continue;
|
| - existingProto[stubProperty] = newProto[stubProperty];
|
| - }
|
| -
|
| - // Update all the existing stub's references to [existing] to
|
| - // [method]. Instance tear-offs are call-by-name, so this isn't
|
| - // necessary for those.
|
| - if (!isStatic) return;
|
| - for (var reference in existingStub) {
|
| - if (existingStub[reference] === existing) {
|
| - existingStub[reference] = method;
|
| - }
|
| - }
|
| - }
|
| - })(holder, stub, existingStub, existing, method));
|
| - }
|
| - } else {
|
| - method = arrayOrFunction;
|
| - holder[name] = method;
|
| - }
|
| - if (isStatic) globalFunctionsAccess[name] = method;
|
| -}""");
|
| - }
|
| -
|
| - Map<OutputUnit, jsAst.Expression> buildDescriptorsForOutputUnits(
|
| - Program program) {
|
| - Map<OutputUnit, jsAst.Expression> outputs =
|
| - new Map<OutputUnit, jsAst.Expression>();
|
| -
|
| - for (Fragment fragment in program.deferredFragments) {
|
| - OutputUnit outputUnit = fragment.outputUnit;
|
| -
|
| - Map<Element, ClassBuilder> descriptors = elementDescriptors[fragment];
|
| -
|
| - if (descriptors != null && descriptors.isNotEmpty) {
|
| - Iterable<LibraryElement> libraries =
|
| - task.outputLibraryLists[outputUnit];
|
| - if (libraries == null) libraries = [];
|
| -
|
| - // TODO(johnniwinther): Avoid creating [CodeBuffer]s.
|
| - List<jsAst.Expression> parts = <jsAst.Expression>[];
|
| - for (LibraryElement library in Elements.sortedByPosition(libraries)) {
|
| - parts.add(generateLibraryDescriptor(library, fragment));
|
| - descriptors.remove(library);
|
| - }
|
| -
|
| - outputs[outputUnit] = new jsAst.ArrayInitializer(parts);
|
| - }
|
| - }
|
| -
|
| - return outputs;
|
| - }
|
| -
|
| - void finalizeTokensInAst(jsAst.Program main,
|
| - Iterable<jsAst.Program> deferredParts) {
|
| - jsAst.TokenCounter counter = new jsAst.TokenCounter();
|
| - counter.countTokens(main);
|
| - deferredParts.forEach(counter.countTokens);
|
| - task.metadataCollector.finalizeTokens();
|
| - if (backend.namer is jsAst.TokenFinalizer) {
|
| - var finalizer = backend.namer;
|
| - finalizer.finalizeTokens();
|
| - }
|
| - }
|
| -
|
| - int emitProgram(ProgramBuilder programBuilder) {
|
| - Program program = programBuilder.buildProgram(
|
| - storeFunctionTypesInMetadata: true);
|
| -
|
| - assembleProgram(program);
|
| -
|
| - // Construct the ASTs for all deferred output units.
|
| - Map<OutputUnit, jsAst.Program> deferredParts =
|
| - buildOutputAstForDeferredCode(program);
|
| -
|
| - Map<OutputUnit, _DeferredOutputUnitHash> deferredHashTokens =
|
| - new Map<OutputUnit, _DeferredOutputUnitHash>.fromIterables(
|
| - deferredParts.keys,
|
| - deferredParts.keys.map((OutputUnit unit) {
|
| - return new _DeferredOutputUnitHash(unit);
|
| - })
|
| - );
|
| -
|
| - jsAst.Program mainOutput =
|
| - buildOutputAstForMain(program, deferredHashTokens);
|
| -
|
| - finalizeTokensInAst(mainOutput, deferredParts.values);
|
| -
|
| - // Emit deferred units first, so we have their hashes.
|
| - // Map from OutputUnit to a hash of its content. The hash uniquely
|
| - // identifies the code of the output-unit. It does not include
|
| - // boilerplate JS code, like the sourcemap directives or the hash
|
| - // itself.
|
| - Map<OutputUnit, String> deferredLoadHashes =
|
| - emitDeferredOutputUnits(deferredParts);
|
| -
|
| - deferredHashTokens.forEach((OutputUnit key, _DeferredOutputUnitHash token) {
|
| - token.setHash(deferredLoadHashes[key]);
|
| - });
|
| - emitMainOutputUnit(program.mainFragment.outputUnit, mainOutput);
|
| -
|
| - if (backend.requiresPreamble &&
|
| - !backend.htmlLibraryIsLoaded) {
|
| - compiler.reportHint(NO_LOCATION_SPANNABLE, MessageKind.PREAMBLE);
|
| - }
|
| - // Return the total program size.
|
| - return outputBuffers.values.fold(0, (a, b) => a + b.length);
|
| - }
|
| -
|
| - String generateSourceMapTag(Uri sourceMapUri, Uri fileUri) {
|
| - if (sourceMapUri != null && fileUri != null) {
|
| - String sourceMapFileName = relativize(fileUri, sourceMapUri, false);
|
| - return '''
|
| -
|
| -//# sourceMappingURL=$sourceMapFileName
|
| -''';
|
| - }
|
| - return '';
|
| - }
|
| -
|
| - ClassBuilder getElementDescriptor(Element element, Fragment fragment) {
|
| - Element owner = element.library;
|
| - bool isClass = false;
|
| - if (!element.isLibrary && !element.isTopLevel && !element.isNative) {
|
| - // For static (not top level) elements, record their code in a buffer
|
| - // specific to the class. For now, not supported for native classes and
|
| - // native elements.
|
| - ClassElement cls =
|
| - element.enclosingClassOrCompilationUnit.declaration;
|
| - if (compiler.codegenWorld.directlyInstantiatedClasses.contains(cls) &&
|
| - !cls.isNative &&
|
| - compiler.deferredLoadTask.outputUnitForElement(element) ==
|
| - compiler.deferredLoadTask.outputUnitForElement(cls)) {
|
| - owner = cls;
|
| - }
|
| - }
|
| - if (owner == null) {
|
| - compiler.internalError(element, 'Owner is null.');
|
| - }
|
| - return elementDescriptors
|
| - .putIfAbsent(fragment, () => new Map<Element, ClassBuilder>())
|
| - .putIfAbsent(owner, () {
|
| - return new ClassBuilder(owner, namer, owner.isClass);
|
| - });
|
| - }
|
| -
|
| - /// Emits support-code for deferred loading into [output].
|
| - jsAst.Statement buildDeferredBoilerPlate(
|
| - Map<OutputUnit, _DeferredOutputUnitHash> deferredLoadHashes) {
|
| - List<jsAst.Statement> parts = <jsAst.Statement>[];
|
| -
|
| - parts.add(js.statement('''
|
| - {
|
| - // Function for checking if a hunk is loaded given its hash.
|
| - #isHunkLoaded = function(hunkHash) {
|
| - return !!$deferredInitializers[hunkHash];
|
| - };
|
| - #deferredInitialized = new Object(null);
|
| - // Function for checking if a hunk is initialized given its hash.
|
| - #isHunkInitialized = function(hunkHash) {
|
| - return #deferredInitialized[hunkHash];
|
| - };
|
| - // Function for initializing a loaded hunk, given its hash.
|
| - #initializeLoadedHunk = function(hunkHash) {
|
| - $deferredInitializers[hunkHash](
|
| - #globalsHolder, ${namer.currentIsolate});
|
| - #deferredInitialized[hunkHash] = true;
|
| - };
|
| - }
|
| - ''', {"globalsHolder": globalsHolder,
|
| - "isHunkLoaded": generateEmbeddedGlobalAccess(
|
| - embeddedNames.IS_HUNK_LOADED),
|
| - "isHunkInitialized": generateEmbeddedGlobalAccess(
|
| - embeddedNames.IS_HUNK_INITIALIZED),
|
| - "initializeLoadedHunk": generateEmbeddedGlobalAccess(
|
| - embeddedNames.INITIALIZE_LOADED_HUNK),
|
| - "deferredInitialized": generateEmbeddedGlobalAccess(
|
| - embeddedNames.DEFERRED_INITIALIZED)}));
|
| -
|
| - // Write a javascript mapping from Deferred import load ids (derrived
|
| - // from the import prefix.) to a list of lists of uris of hunks to load,
|
| - // and a corresponding mapping to a list of hashes used by
|
| - // INITIALIZE_LOADED_HUNK and IS_HUNK_LOADED.
|
| - Map<String, List<jsAst.LiteralString>> deferredLibraryUris =
|
| - new Map<String, List<jsAst.LiteralString>>();
|
| - Map<String, List<_DeferredOutputUnitHash>> deferredLibraryHashes =
|
| - new Map<String, List<_DeferredOutputUnitHash>>();
|
| - compiler.deferredLoadTask.hunksToLoad.forEach(
|
| - (String loadId, List<OutputUnit>outputUnits) {
|
| - List<jsAst.LiteralString> uris = new List<jsAst.LiteralString>();
|
| - List<_DeferredOutputUnitHash> hashes =
|
| - new List<_DeferredOutputUnitHash>();
|
| - deferredLibraryHashes[loadId] = new List<_DeferredOutputUnitHash>();
|
| - for (OutputUnit outputUnit in outputUnits) {
|
| - uris.add(js.escapedString(
|
| - backend.deferredPartFileName(outputUnit.name)));
|
| - hashes.add(deferredLoadHashes[outputUnit]);
|
| - }
|
| -
|
| - deferredLibraryUris[loadId] = uris;
|
| - deferredLibraryHashes[loadId] = hashes;
|
| - });
|
| -
|
| - void emitMapping(String name, Map<String, List<jsAst.Expression>> mapping) {
|
| - List<jsAst.Property> properties = new List<jsAst.Property>();
|
| - mapping.forEach((String key, List<jsAst.Expression> values) {
|
| - properties.add(new jsAst.Property(js.escapedString(key),
|
| - new jsAst.ArrayInitializer(values)));
|
| - });
|
| - jsAst.Node initializer =
|
| - new jsAst.ObjectInitializer(properties, isOneLiner: true);
|
| -
|
| - jsAst.Node globalName = generateEmbeddedGlobalAccess(name);
|
| - parts.add(js.statement("# = #", [globalName, initializer]));
|
| - }
|
| -
|
| - emitMapping(embeddedNames.DEFERRED_LIBRARY_URIS, deferredLibraryUris);
|
| - emitMapping(embeddedNames.DEFERRED_LIBRARY_HASHES,
|
| - deferredLibraryHashes);
|
| -
|
| - return new jsAst.Block(parts);
|
| - }
|
| -
|
| - Map <OutputUnit, jsAst.Program> buildOutputAstForDeferredCode(
|
| - Program program) {
|
| - if (!program.isSplit) return const <OutputUnit, jsAst.Program>{};
|
| -
|
| - Map<OutputUnit, jsAst.Program> result =
|
| - new Map<OutputUnit, jsAst.Program>();
|
| -
|
| - Map<OutputUnit, jsAst.Expression> deferredAsts =
|
| - buildDescriptorsForOutputUnits(program);
|
| -
|
| - for (Fragment fragment in program.deferredFragments) {
|
| - OutputUnit outputUnit = fragment.outputUnit;
|
| - jsAst.Expression libraryDescriptor = deferredAsts[outputUnit];
|
| - List<jsAst.Statement> body = <jsAst.Statement>[];
|
| -
|
| - // No renaming in the top-level function to save the locals for the
|
| - // nested context where they will be used more.
|
| - body.add(js.comment("/* ::norenaming:: "));
|
| -
|
| - for (String globalObject in Namer.reservedGlobalObjectNames) {
|
| - body.add(js.statement('var #object = #globalsHolder.#object;',
|
| - {'globalsHolder': globalsHolder,
|
| - 'object': globalObject}));
|
| - }
|
| - body..add(js.statement('var init = #globalsHolder.init;',
|
| - {'globalsHolder': globalsHolder}))
|
| - ..add(js.statement('var $setupProgramName = '
|
| - '#globalsHolder.$setupProgramName;',
|
| - {'globalsHolder': globalsHolder}))
|
| - ..add(js.statement('var ${namer.isolateName} = '
|
| - '#globalsHolder.${namer.isolateName};',
|
| - {'globalsHolder': globalsHolder}));
|
| - String typesAccess =
|
| - generateEmbeddedGlobalAccessString(embeddedNames.TYPES);
|
| - if (libraryDescriptor != null) {
|
| - // The argument to reflectionDataParser is assigned to a temporary
|
| - // 'dart' so that 'dart.' will appear as the prefix to dart methods
|
| - // in stack traces and profile entries.
|
| - body.add(js.statement('var dart = #', libraryDescriptor));
|
| -
|
| - if (compiler.useContentSecurityPolicy) {
|
| - body.add(buildCspPrecompiledFunctionFor(outputUnit));
|
| - }
|
| - body.add(
|
| - js.statement('$setupProgramName(dart, ${typesAccess}.length);'));
|
| - }
|
| -
|
| - body..add(buildMetadata(program, outputUnit))
|
| - ..add(js.statement('${typesAccess}.push.apply(${typesAccess}, '
|
| - '${namer.deferredTypesName});'));
|
| -
|
| - // Set the currentIsolate variable to the current isolate (which is
|
| - // provided as second argument).
|
| - body.add(js.statement("${namer.currentIsolate} = arguments[1];"));
|
| -
|
| - body.add(buildCompileTimeConstants(fragment.constants,
|
| - isMainFragment: false));
|
| - body.add(buildStaticNonFinalFieldInitializations(outputUnit));
|
| -
|
| - List<jsAst.Statement> statements = <jsAst.Statement>[];
|
| -
|
| - statements
|
| - ..add(buildGeneratedBy())
|
| - ..add(js.statement('${deferredInitializers}.current = '
|
| - """function (#) {
|
| - #
|
| - }
|
| - """, [globalsHolder, body]));
|
| -
|
| - result[outputUnit] = new jsAst.Program(statements);
|
| - }
|
| -
|
| - return result;
|
| - }
|
| -
|
| - /// Returns a map from OutputUnit to a hash of its content. The hash uniquely
|
| - /// identifies the code of the output-unit. It does not include
|
| - /// boilerplate JS code, like the sourcemap directives or the hash
|
| - /// itself.
|
| - Map<OutputUnit, String> emitDeferredOutputUnits(
|
| - Map<OutputUnit, jsAst.Program> outputAsts) {
|
| -
|
| - Map<OutputUnit, String> hunkHashes = new Map<OutputUnit, String>();
|
| -
|
| - for (OutputUnit outputUnit in outputAsts.keys) {
|
| - List<CodeOutputListener> outputListeners = <CodeOutputListener>[];
|
| - Hasher hasher = new Hasher();
|
| - outputListeners.add(hasher);
|
| -
|
| - LineColumnCollector lineColumnCollector;
|
| - if (generateSourceMap) {
|
| - lineColumnCollector = new LineColumnCollector();
|
| - outputListeners.add(lineColumnCollector);
|
| - }
|
| -
|
| - String partPrefix =
|
| - backend.deferredPartFileName(outputUnit.name, addExtension: false);
|
| - CodeOutput output = new StreamCodeOutput(
|
| - compiler.outputProvider(partPrefix, 'part.js'),
|
| - outputListeners);
|
| -
|
| - outputBuffers[outputUnit] = output;
|
| -
|
| - output.addBuffer(jsAst.prettyPrint(outputAsts[outputUnit],
|
| - compiler,
|
| - monitor: compiler.dumpInfoTask));
|
| -
|
| - // Make a unique hash of the code (before the sourcemaps are added)
|
| - // This will be used to retrieve the initializing function from the global
|
| - // variable.
|
| - String hash = hasher.getHash();
|
| -
|
| - output.add('$N${deferredInitializers}["$hash"]$_=$_'
|
| - '${deferredInitializers}.current$N');
|
| -
|
| - if (generateSourceMap) {
|
| - Uri mapUri, partUri;
|
| - Uri sourceMapUri = compiler.sourceMapUri;
|
| - Uri outputUri = compiler.outputUri;
|
| -
|
| - String partName = "$partPrefix.part";
|
| -
|
| - if (sourceMapUri != null) {
|
| - String mapFileName = partName + ".js.map";
|
| - List<String> mapSegments = sourceMapUri.pathSegments.toList();
|
| - mapSegments[mapSegments.length - 1] = mapFileName;
|
| - mapUri = compiler.sourceMapUri.replace(pathSegments: mapSegments);
|
| - }
|
| -
|
| - if (outputUri != null) {
|
| - String partFileName = partName + ".js";
|
| - List<String> partSegments = outputUri.pathSegments.toList();
|
| - partSegments[partSegments.length - 1] = partFileName;
|
| - partUri = compiler.outputUri.replace(pathSegments: partSegments);
|
| - }
|
| -
|
| - output.add(generateSourceMapTag(mapUri, partUri));
|
| - output.close();
|
| - outputSourceMap(output, lineColumnCollector, partName,
|
| - mapUri, partUri);
|
| - } else {
|
| - output.close();
|
| - }
|
| -
|
| - hunkHashes[outputUnit] = hash;
|
| - }
|
| - return hunkHashes;
|
| - }
|
| -
|
| - jsAst.Comment buildGeneratedBy() {
|
| - String suffix = '';
|
| - if (compiler.hasBuildId) suffix = ' version: ${compiler.buildId}';
|
| - String msg = '// Generated by dart2js, the Dart to JavaScript '
|
| - 'compiler$suffix.';
|
| - return new jsAst.Comment(msg);
|
| - }
|
| -
|
| - void outputSourceMap(CodeOutput output,
|
| - LineColumnProvider lineColumnProvider,
|
| - String name,
|
| - [Uri sourceMapUri,
|
| - Uri fileUri]) {
|
| - if (!generateSourceMap) return;
|
| - // Create a source file for the compilation output. This allows using
|
| - // [:getLine:] to transform offsets to line numbers in [SourceMapBuilder].
|
| - SourceMapBuilder sourceMapBuilder =
|
| - new SourceMapBuilder(sourceMapUri, fileUri, lineColumnProvider);
|
| - output.forEachSourceLocation(sourceMapBuilder.addMapping);
|
| - String sourceMap = sourceMapBuilder.build();
|
| - compiler.outputProvider(name, 'js.map')
|
| - ..add(sourceMap)
|
| - ..close();
|
| - }
|
| -
|
| - void outputDeferredMap() {
|
| - Map<String, dynamic> mapping = new Map<String, dynamic>();
|
| - // Json does not support comments, so we embed the explanation in the
|
| - // data.
|
| - mapping["_comment"] = "This mapping shows which compiled `.js` files are "
|
| - "needed for a given deferred library import.";
|
| - mapping.addAll(compiler.deferredLoadTask.computeDeferredMap());
|
| - compiler.outputProvider(compiler.deferredMapUri.path, 'deferred_map')
|
| - ..add(const JsonEncoder.withIndent(" ").convert(mapping))
|
| - ..close();
|
| - }
|
| -
|
| - void invalidateCaches() {
|
| - if (!compiler.hasIncrementalSupport) return;
|
| - if (cachedElements.isEmpty) return;
|
| - for (Element element in compiler.enqueuer.codegen.newlyEnqueuedElements) {
|
| - if (element.isInstanceMember) {
|
| - cachedClassBuilders.remove(element.enclosingClass);
|
| -
|
| - nativeEmitter.cachedBuilders.remove(element.enclosingClass);
|
| -
|
| - }
|
| - }
|
| - }
|
| -}
|
|
|