Avoid inlining constants that are used via a deferred import.
In order to avoid values from a deferred library to leak into the source code, we create an intermediate constant and refer via that. This is also done for non-primitive constants, so that for a case like:
main.dart:
import "lib.dart" deferred as lib;
class C { const C(); }
main() {
print(const C());
print(lib.C1);
}
lib.dart:
import "main.dart" as main;
const C1 = const main.C();
The main output file will not reveal that we are printing the same constant twice.
This CL also changes the constant emitter to be split between a literal emitter and a reference emitter, instead of a initializer emitter and a reference emitter.
R=johnniwinther@google.com
Review URL: https://codereview.chromium.org//256453004
git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@36286 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
@@ -18,6 +18,7 @@ abstract class ConstantVisitor<R> {
|
||||
R visitType(TypeConstant constant);
|
||||
R visitInterceptor(InterceptorConstant constant);
|
||||
R visitDummy(DummyConstant constant);
|
||||
R visitDeferred(DeferredConstant constant);
|
||||
}
|
||||
|
||||
abstract class Constant {
|
||||
@@ -652,3 +653,36 @@ class ConstructedConstant extends ObjectConstant {
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/// A reference to a constant in another output unit.
|
||||
/// Used for referring to deferred constants.
|
||||
class DeferredConstant extends Constant {
|
||||
DeferredConstant(this.referenced, this.prefix);
|
||||
|
||||
final Constant referenced;
|
||||
final PrefixElement prefix;
|
||||
|
||||
bool get isReference => true;
|
||||
|
||||
bool operator ==(other) {
|
||||
return other is DeferredConstant
|
||||
&& referenced == other.referenced
|
||||
&& prefix == other.prefix;
|
||||
}
|
||||
|
||||
get hashCode => (referenced.hashCode * 17 + prefix.hashCode) & 0x3fffffff;
|
||||
|
||||
List<Constant> getDependencies() => <Constant>[referenced];
|
||||
|
||||
accept(ConstantVisitor visitor) => visitor.visitDeferred(this);
|
||||
|
||||
DartType computeType(Compiler compiler) => referenced.computeType(compiler);
|
||||
|
||||
ti.TypeMask computeMask(Compiler compiler) {
|
||||
return referenced.computeMask(compiler);
|
||||
}
|
||||
|
||||
String toString() {
|
||||
return 'DeferredConstant($referenced)';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ import 'resolution/resolution.dart';
|
||||
import 'resolution/class_members.dart' show MembersCreator;
|
||||
import 'source_file.dart' show SourceFile;
|
||||
import 'js/js.dart' as js;
|
||||
import 'deferred_load.dart' show DeferredLoadTask;
|
||||
import 'deferred_load.dart' show DeferredLoadTask, OutputUnit;
|
||||
import 'mirrors_used.dart' show MirrorUsageAnalyzerTask;
|
||||
import 'dump_info.dart';
|
||||
import 'tracer.dart' show Tracer;
|
||||
|
||||
@@ -11,6 +11,7 @@ import 'dart2jslib.dart' show
|
||||
Constant,
|
||||
ConstructedConstant,
|
||||
MessageKind,
|
||||
DeferredConstant,
|
||||
StringConstant,
|
||||
invariant;
|
||||
|
||||
@@ -189,6 +190,13 @@ class DeferredLoadTask extends CompilerTask {
|
||||
return outputUnitForElement(e1) == outputUnitForElement(e2);
|
||||
}
|
||||
|
||||
void registerConstantDeferredUse(DeferredConstant constant,
|
||||
PrefixElement prefix) {
|
||||
OutputUnit outputUnit = new OutputUnit();
|
||||
outputUnit.imports.add(prefix.deferredImport);
|
||||
_constantToOutputUnit[constant] = outputUnit;
|
||||
}
|
||||
|
||||
/// Mark that [import] is part of the [OutputputUnit] for [element].
|
||||
///
|
||||
/// [element] can be either a [Constant] or an [Element].
|
||||
|
||||
@@ -4,14 +4,13 @@
|
||||
|
||||
part of js_backend;
|
||||
|
||||
class ConstantEmitter {
|
||||
class ConstantEmitter {
|
||||
ConstantReferenceEmitter _referenceEmitter;
|
||||
ConstantInitializerEmitter _initializerEmitter;
|
||||
ConstantLiteralEmitter _literalEmitter;
|
||||
|
||||
ConstantEmitter(Compiler compiler, Namer namer) {
|
||||
_referenceEmitter = new ConstantReferenceEmitter(compiler, namer);
|
||||
_initializerEmitter = new ConstantInitializerEmitter(
|
||||
compiler, namer, _referenceEmitter);
|
||||
_literalEmitter = new ConstantLiteralEmitter(compiler, namer, this);
|
||||
_referenceEmitter = new ConstantReferenceEmitter(compiler, namer, this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -23,19 +22,28 @@ class ConstantEmitter {
|
||||
return _referenceEmitter.generate(constant);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a literal expression that evaluates to the constant. Uses a
|
||||
* canonical name unless the constant can be emitted multiple times (as for
|
||||
* numbers and strings).
|
||||
*/
|
||||
jsAst.Expression literal(Constant constant) {
|
||||
return _literalEmitter.generate(constant);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an expression like [reference], but the expression is valid
|
||||
* during isolate initialization.
|
||||
*/
|
||||
jsAst.Expression referenceInInitializationContext(Constant constant) {
|
||||
return _referenceEmitter.generateInInitializationContext(constant);
|
||||
return _referenceEmitter.generate(constant);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an expression used to initialize a canonicalized constant.
|
||||
*/
|
||||
jsAst.Expression initializationExpression(Constant constant) {
|
||||
return _initializerEmitter.generate(constant);
|
||||
return _literalEmitter.generate(constant);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,13 +55,112 @@ class ConstantReferenceEmitter implements ConstantVisitor<jsAst.Expression> {
|
||||
final Compiler compiler;
|
||||
final Namer namer;
|
||||
|
||||
ConstantReferenceEmitter(this.compiler, this.namer);
|
||||
final ConstantEmitter constantEmitter;
|
||||
|
||||
ConstantReferenceEmitter(this.compiler, this.namer, this.constantEmitter);
|
||||
|
||||
jsAst.Expression generate(Constant constant) {
|
||||
return _visit(constant);
|
||||
}
|
||||
|
||||
jsAst.Expression generateInInitializationContext(Constant constant) {
|
||||
jsAst.Expression _visit(Constant constant) {
|
||||
return constant.accept(this);
|
||||
}
|
||||
|
||||
jsAst.Expression emitCanonicalVersion(Constant constant) {
|
||||
String name = namer.constantName(constant);
|
||||
return new jsAst.PropertyAccess.field(
|
||||
new jsAst.VariableUse(namer.globalObjectForConstant(constant)), name);
|
||||
}
|
||||
|
||||
jsAst.Expression literal(Constant constant) {
|
||||
return constantEmitter.literal(constant);
|
||||
}
|
||||
|
||||
jsAst.Expression visitFunction(FunctionConstant constant) {
|
||||
return namer.isolateStaticClosureAccess(constant.element);
|
||||
}
|
||||
|
||||
jsAst.Expression visitNull(NullConstant constant) {
|
||||
return literal(constant);
|
||||
}
|
||||
|
||||
jsAst.Expression visitInt(IntConstant constant) {
|
||||
return literal(constant);
|
||||
}
|
||||
|
||||
jsAst.Expression visitDouble(DoubleConstant constant) {
|
||||
return literal(constant);
|
||||
}
|
||||
|
||||
jsAst.Expression visitTrue(TrueConstant constant) {
|
||||
return literal(constant);
|
||||
}
|
||||
|
||||
jsAst.Expression visitFalse(FalseConstant constant) {
|
||||
return literal(constant);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the contents of the quoted string to a [CodeBuffer] in
|
||||
* a form that is valid as JavaScript string literal content.
|
||||
* The string is assumed quoted by double quote characters.
|
||||
*/
|
||||
jsAst.Expression visitString(StringConstant constant) {
|
||||
// TODO(sra): If the string is long *and repeated* (and not on a hot path)
|
||||
// then it should be assigned to a name. We don't have reference counts (or
|
||||
// profile information) here, so this is the wrong place.
|
||||
return literal(constant);
|
||||
}
|
||||
|
||||
jsAst.Expression visitList(ListConstant constant) {
|
||||
return emitCanonicalVersion(constant);
|
||||
}
|
||||
|
||||
jsAst.Expression visitMap(MapConstant constant) {
|
||||
return emitCanonicalVersion(constant);
|
||||
}
|
||||
|
||||
jsAst.Expression visitType(TypeConstant constant) {
|
||||
return emitCanonicalVersion(constant);
|
||||
}
|
||||
|
||||
jsAst.Expression visitConstructed(ConstructedConstant constant) {
|
||||
return emitCanonicalVersion(constant);
|
||||
}
|
||||
|
||||
jsAst.Expression visitInterceptor(InterceptorConstant constant) {
|
||||
return emitCanonicalVersion(constant);
|
||||
}
|
||||
|
||||
jsAst.Expression visitDummy(DummyConstant constant) {
|
||||
return literal(constant);
|
||||
}
|
||||
|
||||
jsAst.Expression visitDeferred(DeferredConstant constant) {
|
||||
return emitCanonicalVersion(constant);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Visitor for generating JavaScript expressions that litterally represent
|
||||
* [Constant]s. These can be used for inlining constants or in initializers.
|
||||
* Do not use directly, use methods from [ConstantEmitter].
|
||||
*/
|
||||
class ConstantLiteralEmitter implements ConstantVisitor<jsAst.Expression> {
|
||||
|
||||
// Matches blank lines, comment lines and trailing comments that can't be part
|
||||
// of a string.
|
||||
static final RegExp COMMENT_RE =
|
||||
new RegExp(r'''^ *(//.*)?\n| *//[^''"\n]*$''' , multiLine: true);
|
||||
|
||||
final Compiler compiler;
|
||||
final Namer namer;
|
||||
final ConstantEmitter constantEmitter;
|
||||
|
||||
ConstantLiteralEmitter(this.compiler, this.namer, this.constantEmitter);
|
||||
|
||||
jsAst.Expression generate(Constant constant) {
|
||||
return _visit(constant);
|
||||
}
|
||||
|
||||
@@ -62,7 +169,9 @@ class ConstantReferenceEmitter implements ConstantVisitor<jsAst.Expression> {
|
||||
}
|
||||
|
||||
jsAst.Expression visitFunction(FunctionConstant constant) {
|
||||
return namer.isolateStaticClosureAccess(constant.element);
|
||||
compiler.internalError(NO_LOCATION_SPANNABLE,
|
||||
"The function constant does not need specific JS code.");
|
||||
return null;
|
||||
}
|
||||
|
||||
jsAst.Expression visitNull(NullConstant constant) {
|
||||
@@ -110,104 +219,11 @@ class ConstantReferenceEmitter implements ConstantVisitor<jsAst.Expression> {
|
||||
* The string is assumed quoted by double quote characters.
|
||||
*/
|
||||
jsAst.Expression visitString(StringConstant constant) {
|
||||
// TODO(sra): If the string is long *and repeated* (and not on a hot path)
|
||||
// then it should be assigned to a name. We don't have reference counts (or
|
||||
// profile information) here, so this is the wrong place.
|
||||
StringBuffer sb = new StringBuffer();
|
||||
writeJsonEscapedCharsOn(constant.value.slowToString(), sb);
|
||||
return new jsAst.LiteralString('"$sb"');
|
||||
}
|
||||
|
||||
jsAst.Expression emitCanonicalVersion(Constant constant) {
|
||||
String name = namer.constantName(constant);
|
||||
return new jsAst.PropertyAccess.field(
|
||||
new jsAst.VariableUse(namer.globalObjectForConstant(constant)), name);
|
||||
}
|
||||
|
||||
jsAst.Expression visitList(ListConstant constant) {
|
||||
return emitCanonicalVersion(constant);
|
||||
}
|
||||
|
||||
jsAst.Expression visitMap(MapConstant constant) {
|
||||
return emitCanonicalVersion(constant);
|
||||
}
|
||||
|
||||
jsAst.Expression visitType(TypeConstant constant) {
|
||||
return emitCanonicalVersion(constant);
|
||||
}
|
||||
|
||||
jsAst.Expression visitConstructed(ConstructedConstant constant) {
|
||||
return emitCanonicalVersion(constant);
|
||||
}
|
||||
|
||||
jsAst.Expression visitInterceptor(InterceptorConstant constant) {
|
||||
return emitCanonicalVersion(constant);
|
||||
}
|
||||
|
||||
jsAst.Expression visitDummy(DummyConstant constant) {
|
||||
return new jsAst.LiteralNumber('0');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Visitor for generating JavaScript expressions to initialize [Constant]s.
|
||||
* Do not use directly; use methods from [ConstantEmitter].
|
||||
*/
|
||||
class ConstantInitializerEmitter implements ConstantVisitor<jsAst.Expression> {
|
||||
final Compiler compiler;
|
||||
final Namer namer;
|
||||
final ConstantReferenceEmitter referenceEmitter;
|
||||
|
||||
// Matches blank lines, comment lines and trailing comments that can't be part
|
||||
// of a string.
|
||||
static final RegExp COMMENT_RE =
|
||||
new RegExp(r'''^ *(//.*)?\n| *//[^''"\n]*$''' , multiLine: true);
|
||||
|
||||
ConstantInitializerEmitter(this.compiler, this.namer, this.referenceEmitter);
|
||||
|
||||
jsAst.Expression generate(Constant constant) {
|
||||
return _visit(constant);
|
||||
}
|
||||
|
||||
jsAst.Expression _visit(Constant constant) {
|
||||
return constant.accept(this);
|
||||
}
|
||||
|
||||
jsAst.Expression _reference(Constant constant) {
|
||||
return referenceEmitter.generateInInitializationContext(constant);
|
||||
}
|
||||
|
||||
jsAst.Expression visitFunction(FunctionConstant constant) {
|
||||
compiler.internalError(NO_LOCATION_SPANNABLE,
|
||||
"The function constant does not need specific JS code.");
|
||||
return null;
|
||||
}
|
||||
|
||||
jsAst.Expression visitNull(NullConstant constant) {
|
||||
return _reference(constant);
|
||||
}
|
||||
|
||||
jsAst.Expression visitInt(IntConstant constant) {
|
||||
return _reference(constant);
|
||||
}
|
||||
|
||||
jsAst.Expression visitDouble(DoubleConstant constant) {
|
||||
return _reference(constant);
|
||||
}
|
||||
|
||||
jsAst.Expression visitTrue(TrueConstant constant) {
|
||||
return _reference(constant);
|
||||
}
|
||||
|
||||
jsAst.Expression visitFalse(FalseConstant constant) {
|
||||
return _reference(constant);
|
||||
}
|
||||
|
||||
jsAst.Expression visitString(StringConstant constant) {
|
||||
// TODO(sra): Some larger strings are worth sharing.
|
||||
return _reference(constant);
|
||||
}
|
||||
|
||||
jsAst.Expression visitList(ListConstant constant) {
|
||||
jsAst.Expression value = new jsAst.Call(
|
||||
new jsAst.PropertyAccess.field(
|
||||
@@ -231,7 +247,7 @@ class ConstantInitializerEmitter implements ConstantVisitor<jsAst.Expression> {
|
||||
// Keys in literal maps must be emitted in place.
|
||||
jsAst.Literal keyExpression = _visit(key);
|
||||
jsAst.Expression valueExpression =
|
||||
_reference(constant.values[i]);
|
||||
constantEmitter.reference(constant.values[i]);
|
||||
properties.add(new jsAst.Property(keyExpression, valueExpression));
|
||||
}
|
||||
return new jsAst.ObjectInitializer(properties);
|
||||
@@ -241,9 +257,9 @@ class ConstantInitializerEmitter implements ConstantVisitor<jsAst.Expression> {
|
||||
List<jsAst.Expression> data = <jsAst.Expression>[];
|
||||
for (int i = 0; i < constant.keys.entries.length; i++) {
|
||||
jsAst.Expression keyExpression =
|
||||
_reference(constant.keys.entries[i]);
|
||||
constantEmitter.reference(constant.keys.entries[i]);
|
||||
jsAst.Expression valueExpression =
|
||||
_reference(constant.values[i]);
|
||||
constantEmitter.reference(constant.values[i]);
|
||||
data.add(keyExpression);
|
||||
data.add(valueExpression);
|
||||
}
|
||||
@@ -266,10 +282,10 @@ class ConstantInitializerEmitter implements ConstantVisitor<jsAst.Expression> {
|
||||
} else if (field.name == MapConstant.JS_OBJECT_NAME) {
|
||||
arguments.add(jsMap());
|
||||
} else if (field.name == MapConstant.KEYS_NAME) {
|
||||
arguments.add(_reference(constant.keys));
|
||||
arguments.add(constantEmitter.reference(constant.keys));
|
||||
} else if (field.name == MapConstant.PROTO_VALUE) {
|
||||
assert(constant.protoValue != null);
|
||||
arguments.add(_reference(constant.protoValue));
|
||||
arguments.add(constantEmitter.reference(constant.protoValue));
|
||||
} else if (field.name == MapConstant.JS_DATA_NAME) {
|
||||
arguments.add(jsGeneralMap());
|
||||
} else {
|
||||
@@ -316,7 +332,7 @@ class ConstantInitializerEmitter implements ConstantVisitor<jsAst.Expression> {
|
||||
}
|
||||
|
||||
jsAst.Expression visitDummy(DummyConstant constant) {
|
||||
return _reference(constant);
|
||||
return new jsAst.LiteralNumber('0');
|
||||
}
|
||||
|
||||
jsAst.Expression visitConstructed(ConstructedConstant constant) {
|
||||
@@ -340,7 +356,7 @@ class ConstantInitializerEmitter implements ConstantVisitor<jsAst.Expression> {
|
||||
List<jsAst.Expression> _array(List<Constant> values) {
|
||||
List<jsAst.Expression> valueList = <jsAst.Expression>[];
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
valueList.add(_reference(values[i]));
|
||||
valueList.add(constantEmitter.reference(values[i]));
|
||||
}
|
||||
return valueList;
|
||||
}
|
||||
@@ -363,4 +379,8 @@ class ConstantInitializerEmitter implements ConstantVisitor<jsAst.Expression> {
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
jsAst.Expression visitDeferred(DeferredConstant constant) {
|
||||
return constantEmitter.reference(constant.referenced);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1163,6 +1163,10 @@ class ConstantNamingVisitor implements ConstantVisitor {
|
||||
visitDummy(DummyConstant constant) {
|
||||
add('dummy_receiver');
|
||||
}
|
||||
|
||||
visitDeferred(DeferredConstant constant) {
|
||||
addRoot('Deferred');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1245,6 +1249,11 @@ class ConstantCanonicalHasher implements ConstantVisitor<int> {
|
||||
'DummyReceiverConstant should never be named and never be subconstant');
|
||||
}
|
||||
|
||||
visitDeferred(DeferredConstant constant) {
|
||||
int hash = constant.prefix.hashCode;
|
||||
return _combine(hash, constant.referenced.accept(this));
|
||||
}
|
||||
|
||||
int _hashString(int hash, String s) {
|
||||
int length = s.length;
|
||||
hash = _combine(hash, length);
|
||||
|
||||
@@ -33,6 +33,8 @@ class CodeEmitterTask extends CompilerTask {
|
||||
final Set<ClassElement> neededClasses = new Set<ClassElement>();
|
||||
final Map<OutputUnit, List<ClassElement>> outputClassLists =
|
||||
new Map<OutputUnit, List<ClassElement>>();
|
||||
final Map<OutputUnit, List<Constant>> outputConstantLists =
|
||||
new Map<OutputUnit, List<Constant>>();
|
||||
final List<ClassElement> nativeClasses = <ClassElement>[];
|
||||
final Map<String, String> mangledFieldNames = <String, String>{};
|
||||
final Map<String, String> mangledGlobalFieldNames = <String, String>{};
|
||||
@@ -890,26 +892,9 @@ class CodeEmitterTask extends CompilerTask {
|
||||
}
|
||||
|
||||
void emitCompileTimeConstants(CodeBuffer buffer, OutputUnit outputUnit) {
|
||||
JavaScriptConstantCompiler handler = backend.constants;
|
||||
List<Constant> constants = handler.getConstantsForEmission(
|
||||
compareConstants);
|
||||
Set<Constant> outputUnitConstants = null;
|
||||
// TODO(sigurdm): We shouldn't run through all constants for every
|
||||
// outputUnit.
|
||||
List<Constant> constants = outputConstantLists[outputUnit];
|
||||
if (constants == null) return;
|
||||
for (Constant constant in constants) {
|
||||
if (isConstantInlinedOrAlreadyEmitted(constant)) continue;
|
||||
OutputUnit constantUnit =
|
||||
compiler.deferredLoadTask.outputUnitForConstant(constant);
|
||||
if (constantUnit != outputUnit && constantUnit != null) continue;
|
||||
if (outputUnit != compiler.deferredLoadTask.mainOutputUnit
|
||||
&& constantUnit == null) {
|
||||
// The back-end introduces some constants, like "InterceptorConstant" or
|
||||
// some list constants. They are emitted in the main output-unit, and
|
||||
// ignored otherwise.
|
||||
// TODO(sigurdm): We should track those constants.
|
||||
continue;
|
||||
}
|
||||
|
||||
String name = namer.constantName(constant);
|
||||
if (constant.isList) emitMakeConstantListIfNotEmitted(buffer);
|
||||
jsAst.Expression init = js('#.# = #',
|
||||
@@ -1093,6 +1078,28 @@ class CodeEmitterTask extends CompilerTask {
|
||||
addComment('END invoke [main].', buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute all the constants that must be emitted.
|
||||
*/
|
||||
void computeNeededConstants() {
|
||||
JavaScriptConstantCompiler handler = backend.constants;
|
||||
List<Constant> constants = handler.getConstantsForEmission(
|
||||
compareConstants);
|
||||
for (Constant constant in constants) {
|
||||
if (isConstantInlinedOrAlreadyEmitted(constant)) continue;
|
||||
OutputUnit constantUnit =
|
||||
compiler.deferredLoadTask.outputUnitForConstant(constant);
|
||||
if (constantUnit == null) {
|
||||
// The back-end introduces some constants, like "InterceptorConstant" or
|
||||
// some list constants. They are emitted in the main output-unit.
|
||||
// TODO(sigurdm): We should track those constants.
|
||||
constantUnit = compiler.deferredLoadTask.mainOutputUnit;
|
||||
}
|
||||
outputConstantLists.putIfAbsent(constantUnit, () => new List<Constant>())
|
||||
.add(constant);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute all the classes that must be emitted.
|
||||
*/
|
||||
@@ -1464,6 +1471,7 @@ class CodeEmitterTask extends CompilerTask {
|
||||
// which may need getInterceptor (and one-shot interceptor) methods, so
|
||||
// we have to make sure that [emitGetInterceptorMethods] and
|
||||
// [emitOneShotInterceptors] have been called.
|
||||
computeNeededConstants();
|
||||
emitCompileTimeConstants(mainBuffer, mainOutputUnit);
|
||||
|
||||
// Write a javascript mapping from Deferred import load ids (derrived from
|
||||
|
||||
@@ -2996,7 +2996,16 @@ class SsaBuilder extends ResolvedVisitor {
|
||||
value = backend.constants.getConstantForVariable(element);
|
||||
}
|
||||
if (value != null) {
|
||||
HConstant instruction = graph.addConstant(value, compiler);
|
||||
HConstant instruction;
|
||||
// Constants that are referred via a deferred prefix should be referred
|
||||
// by reference.
|
||||
PrefixElement prefix = compiler.deferredLoadTask
|
||||
.deferredPrefixElement(send, elements);
|
||||
if (prefix != null) {
|
||||
instruction = graph.addDeferredConstant(value, prefix, compiler);
|
||||
} else {
|
||||
instruction = graph.addConstant(value, compiler);
|
||||
}
|
||||
stack.add(instruction);
|
||||
// The inferrer may have found a better type than the constant
|
||||
// handler in the case of lists, because the constant handler
|
||||
|
||||
@@ -184,6 +184,13 @@ class HGraph {
|
||||
return result;
|
||||
}
|
||||
|
||||
HConstant addDeferredConstant(Constant constant, PrefixElement prefix,
|
||||
Compiler compiler) {
|
||||
Constant wrapper = new DeferredConstant(constant, prefix);
|
||||
compiler.deferredLoadTask.registerConstantDeferredUse(wrapper, prefix);
|
||||
return addConstant(wrapper, compiler);
|
||||
}
|
||||
|
||||
HConstant addConstantInt(int i, Compiler compiler) {
|
||||
return addConstant(compiler.backend.constantSystem.createInt(i), compiler);
|
||||
}
|
||||
|
||||
@@ -670,9 +670,15 @@ class SsaValueRangeAnalyzer extends HBaseVisitor implements OptimizationPhase {
|
||||
return range;
|
||||
}
|
||||
|
||||
Range visitConstant(HConstant constant) {
|
||||
if (!constant.isInteger(compiler)) return info.newUnboundRange();
|
||||
NumConstant constantNum = constant.constant;
|
||||
Range visitConstant(HConstant hConstant) {
|
||||
if (!hConstant.isInteger(compiler)) return info.newUnboundRange();
|
||||
Constant constant = hConstant.constant;
|
||||
NumConstant constantNum;
|
||||
if (constant is DeferredConstant) {
|
||||
constantNum = constant.referenced;
|
||||
} else {
|
||||
constantNum = constant;
|
||||
}
|
||||
if (constantNum.isMinusZero) constantNum = new IntConstant(0);
|
||||
Value value = info.newIntValue(constantNum.value);
|
||||
return info.newNormalizedRange(value, value);
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
// 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.
|
||||
|
||||
// Test that the additional runtime type support is output to the right
|
||||
// Files when using deferred loading.
|
||||
|
||||
import 'package:expect/expect.dart';
|
||||
import "package:async_helper/async_helper.dart";
|
||||
import 'memory_source_file_helper.dart';
|
||||
import "dart:async";
|
||||
|
||||
import '../../../sdk/lib/_internal/compiler/implementation/dart2jslib.dart'
|
||||
as dart2js;
|
||||
|
||||
class MemoryOutputSink extends EventSink<String> {
|
||||
StringBuffer mem = new StringBuffer();
|
||||
void add(String event) {
|
||||
mem.write(event);
|
||||
}
|
||||
void addError(String event, [StackTrace stackTrace]) {
|
||||
Expect.isTrue(false);
|
||||
}
|
||||
void close() {}
|
||||
}
|
||||
|
||||
void main() {
|
||||
Uri script = currentDirectory.resolveUri(Platform.script);
|
||||
Uri libraryRoot = script.resolve('../../../sdk/');
|
||||
Uri packageRoot = script.resolve('./packages/');
|
||||
|
||||
var provider = new MemorySourceFileProvider(MEMORY_SOURCE_FILES);
|
||||
var handler = new FormattingDiagnosticHandler(provider);
|
||||
|
||||
Map<String, MemoryOutputSink> outputs = new Map<String, MemoryOutputSink>();
|
||||
|
||||
MemoryOutputSink outputSaver(name, extension) {
|
||||
if (name == '') {
|
||||
name = 'main';
|
||||
}
|
||||
return outputs.putIfAbsent("$name.$extension", () {
|
||||
return new MemoryOutputSink();
|
||||
});
|
||||
}
|
||||
|
||||
Compiler compiler = new Compiler(provider.readStringFromUri,
|
||||
outputSaver,
|
||||
handler.diagnosticHandler,
|
||||
libraryRoot,
|
||||
packageRoot,
|
||||
[],
|
||||
{});
|
||||
asyncTest(() => compiler.run(Uri.parse('memory:main.dart')).then((_) {
|
||||
String mainOutput = outputs['main.js'].mem.toString();
|
||||
String lib1Output = outputs['out_lib1.part.js'].mem.toString();
|
||||
String lib2Output = outputs['out_lib2.part.js'].mem.toString();
|
||||
String lib12Output = outputs['out_lib1_lib2.part.js'].mem.toString();
|
||||
// Test that the deferred constants are not inlined into the main file.
|
||||
RegExp re1 = new RegExp(r"= .string1");
|
||||
RegExp re2 = new RegExp(r"= .string2");
|
||||
RegExp re3 = new RegExp(r"= 1010");
|
||||
Expect.isTrue(re1.hasMatch(lib1Output));
|
||||
Expect.isTrue(re2.hasMatch(lib1Output));
|
||||
Expect.isTrue(re3.hasMatch(lib1Output));
|
||||
Expect.isFalse(re1.hasMatch(mainOutput));
|
||||
Expect.isFalse(re2.hasMatch(mainOutput));
|
||||
Expect.isFalse(re3.hasMatch(mainOutput));
|
||||
// Test that the non-deferred constant is inlined.
|
||||
Expect.isTrue(new RegExp(r"print\(.string3.\)").hasMatch(mainOutput));
|
||||
Expect.isFalse(new RegExp(r"= .string3").hasMatch(mainOutput));
|
||||
Expect.isTrue(new RegExp(r"print\(.string4.\)").hasMatch(mainOutput));
|
||||
|
||||
// C(1) is shared between main, lib1 and lib2. Test that lib1 and lib2 each
|
||||
// has a reference to it. It is defined in the main output file.
|
||||
Expect.isTrue(new RegExp(r"C.C_1 =").hasMatch(mainOutput));
|
||||
Expect.isFalse(new RegExp(r"= C.C_1").hasMatch(mainOutput));
|
||||
|
||||
Expect.isTrue(new RegExp(r"= C.C_1").hasMatch(lib1Output));
|
||||
Expect.isTrue(new RegExp(r"= C.C_1").hasMatch(lib2Output));
|
||||
|
||||
// C(2) is shared between lib1 and lib2, each of them has their own
|
||||
// reference to it.
|
||||
Expect.isFalse(new RegExp(r"= C.C_2").hasMatch(mainOutput));
|
||||
|
||||
Expect.isTrue(new RegExp(r"= C.C_2").hasMatch(lib1Output));
|
||||
Expect.isTrue(new RegExp(r"= C.C_2").hasMatch(lib2Output));
|
||||
Expect.isTrue(new RegExp(r"C.C_2 =").hasMatch(lib12Output));
|
||||
|
||||
// "string4" is shared between lib1 and lib2, but it can be inlined.
|
||||
Expect.isTrue(new RegExp(r"= .string4").hasMatch(lib1Output));
|
||||
Expect.isTrue(new RegExp(r"= .string4").hasMatch(lib2Output));
|
||||
Expect.isFalse(new RegExp(r"= .string4").hasMatch(lib12Output));
|
||||
}));
|
||||
}
|
||||
|
||||
// Make sure that deferred constants are not inlined into the main hunk.
|
||||
const Map MEMORY_SOURCE_FILES = const {"main.dart": """
|
||||
import "dart:async";
|
||||
|
||||
import 'lib1.dart' deferred as lib1;
|
||||
import 'lib2.dart' deferred as lib2;
|
||||
|
||||
const c = "string3";
|
||||
|
||||
class C {
|
||||
final p;
|
||||
const C(this.p);
|
||||
}
|
||||
|
||||
void main() {
|
||||
lib1.loadLibrary().then((_) {
|
||||
lib2.loadLibrary().then((_) {
|
||||
print(lib1.C1);
|
||||
print(lib1.C2);
|
||||
print(lib1.C.C3);
|
||||
print(c);
|
||||
print(lib1.C4);
|
||||
print(lib2.C4);
|
||||
print(lib1.C5);
|
||||
print(lib2.C5);
|
||||
print(lib1.C6);
|
||||
print(lib2.C6);
|
||||
print("string4");
|
||||
print(const C(1));
|
||||
});
|
||||
});
|
||||
}
|
||||
""", "lib1.dart": """
|
||||
import "main.dart" as main;
|
||||
const C1 = "string1";
|
||||
const C2 = 1010;
|
||||
class C {
|
||||
static const C3 = "string2";
|
||||
}
|
||||
const C4 = "string4";
|
||||
const C5 = const main.C(1);
|
||||
const C6 = const main.C(2);
|
||||
""", "lib2.dart": """
|
||||
import "main.dart" as main;
|
||||
const C4 = "string4";
|
||||
const C5 = const main.C(1);
|
||||
const C6 = const main.C(2);
|
||||
"""};
|
||||
Reference in New Issue
Block a user