Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(37)

Unified Diff: dart/sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart

Issue 24267004: Emit CSP code in separate file. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge
Patch Set: Make tests pass Created 7 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: dart/sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart
diff --git a/dart/sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart b/dart/sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart
index 21df4ae5faf6eae8831df67b122ad7704097d686..f508bfd47aaa92d68fda48c032ae367070e2f03d 100644
--- a/dart/sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart
+++ b/dart/sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart
@@ -90,7 +90,6 @@ typedef void SubstitutionEmitter(Element element, {bool emitNull});
* The code for the containing (used) methods must exist in the [:universe:].
*/
class CodeEmitterTask extends CompilerTask {
- bool needsInheritFunction = false;
bool needsDefineClass = false;
bool needsMixinSupport = false;
bool needsLazyInitializer = false;
@@ -168,6 +167,22 @@ class CodeEmitterTask extends CompilerTask {
new Set<FunctionType>();
/**
+ * 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, and may impact startup time
+ * negatively. So dart2js will emit these functions to a separate file that
+ * can be optionally included to support CSP mode or for faster startup.
+ */
+ List<jsAst.Node> precompiledFunction = <jsAst.Node>[];
+
+ List<jsAst.Expression> precompiledConstructorNames = <jsAst.Expression>[];
+
+ // True if Isolate.makeConstantList is needed.
+ bool hasMakeConstantList = false;
+
+ /**
* For classes and libraries, record code for static/top-level members.
* Later, this code is emitted when the class or library is emitted.
* See [bufferForElement].
@@ -263,8 +278,6 @@ class CodeEmitterTask extends CompilerTask {
=> '${namer.isolateName}.\$finishIsolateConstructor';
String get isolatePropertiesName
=> '${namer.isolateName}.${namer.isolatePropertiesName}';
- String get supportsProtoName
- => 'supportsProto';
String get lazyInitializerName
=> '${namer.isolateName}.\$lazy';
@@ -427,27 +440,24 @@ class CodeEmitterTask extends CompilerTask {
}
/** Needs defineClass to be defined. */
- List buildProtoSupportCheck() {
- // On Firefox and Webkit browsers we can manipulate the __proto__
- // directly. Opera claims to have __proto__ support, but it is buggy.
- // So we have to do more checks.
- // Opera bug was filed as DSK-370158, and fixed as CORE-47615
- // (http://my.opera.com/desktopteam/blog/2012/07/20/more-12-01-fixes).
- // If the browser does not support __proto__ we need to instantiate an
- // object with the correct (internal) prototype set up correctly, and then
- // copy the members.
- // TODO(8541): Remove this work around.
-
+ List buildInheritFrom() {
return [
- js('var $supportsProtoName = false'),
- // js('var tmp = defineClass("c", "c", ["f<"], {}).prototype'),
- //
- // js.if_(js('tmp.__proto__'), [
- // js('tmp.__proto__ = {}'),
- // js.if_(js(r'typeof tmp.get$f != "undefined"'),
- // js('$supportsProtoName = true'))
- // ])
- ];
+ js('var inheritFrom = #',
+ js.fun([], [
+ new jsAst.FunctionDeclaration(
+ new jsAst.VariableDeclaration('tmp'), js.fun([], [])),
+ js('var hasOwnProperty = Object.prototype.hasOwnProperty'),
+ js.return_(js.fun(['constructor', 'superConstructor'], [
+ js('tmp.prototype = superConstructor.prototype'),
+ js('var object = new tmp()'),
+ js('var properties = constructor.prototype'),
+ js.forIn('member', 'properties',
+ js.if_('hasOwnProperty.call(properties, member)',
+ js('object[member] = properties[member]'))),
+ js('object.constructor = constructor'),
+ js('constructor.prototype = object'),
+ js.return_('object')
+ ]))])())];
}
static const MAX_MINIFIED_LENGTH_FOR_DIFF_ENCODING = 4;
@@ -702,17 +712,19 @@ class CodeEmitterTask extends CompilerTask {
js.if_('!init.allClasses', js('init.allClasses = {}')),
js('var allClasses = init.allClasses'),
- js('var hasOwnProperty = Object.prototype.hasOwnProperty'),
- js('var combinedConstructorFunction = '
- '"function \$reflectable(fn){fn.$reflectableField=1;return fn};\\n"'
- '+ "var \$desc;\\n"'),
- js('var constructorsList = []'),
-
optional(
DEBUG_FAST_OBJECTS,
js('print("Number of classes: "'
r' + Object.getOwnPropertyNames($$).length)')),
+ js('var hasOwnProperty = Object.prototype.hasOwnProperty'),
+
+ js.if_('typeof dart_precompiled == "function"',
+ [js('var constructors = dart_precompiled(collectedClasses)')],
+
+ [js('var combinedConstructorFunction = "function \$reflectable(fn){'
+ 'fn.$reflectableField=1;return fn};\\n"+ "var \$desc;\\n"'),
+ js('var constructorsList = []')]),
js.forIn('cls', 'collectedClasses', [
js.if_('hasOwnProperty.call(collectedClasses, cls)', [
js('var desc = collectedClasses[cls]'),
@@ -742,14 +754,9 @@ class CodeEmitterTask extends CompilerTask {
])
]),
- js.if_('typeof fields == "string"', [
- js('var s = fields.split(";")'),
- js('fields = s[1] == "" ? [] : s[1].split(",")'),
- js('supr = s[0]'),
- ], /* else */ [
- js('supr = desc.super'),
- js.if_(r'!!desc.$name', js(r'name = desc.$name'))
- ]),
+ js('var s = fields.split(";")'),
+ js('fields = s[1] == "" ? [] : s[1].split(",")'),
+ js('supr = s[0]'),
optional(needsMixinSupport, js.if_('supr && supr.indexOf("+") > 0', [
js('s = supr.split("+")'),
@@ -763,17 +770,20 @@ class CodeEmitterTask extends CompilerTask {
]),
])),
- js('combinedConstructorFunction += defineClass(name, cls, fields)'),
- js('constructorsList.push(cls)'),
+ js.if_('typeof dart_precompiled != "function"',
+ [js('combinedConstructorFunction +='
+ ' defineClass(name, cls, fields)'),
+ js('constructorsList.push(cls)')]),
js.if_('supr', js('pendingClasses[cls] = supr'))
])
]),
- js('combinedConstructorFunction +='
- ' "return [\\n " + constructorsList.join(",\\n ") + "\\n]"'),
- js('var constructors ='
- ' new Function("\$collectedClasses", combinedConstructorFunction)'
- '(collectedClasses)'),
- js('combinedConstructorFunction = null'),
+ js.if_('typeof dart_precompiled != "function"',
+ [js('combinedConstructorFunction +='
+ ' "return [\\n " + constructorsList.join(",\\n ") + "\\n]"'),
+ js('var constructors ='
+ ' new Function("\$collectedClasses", combinedConstructorFunction)'
+ '(collectedClasses)'),
+ js('combinedConstructorFunction = null')]),
js.for_('var i = 0', 'i < constructors.length', 'i++', [
js('var constructor = constructors[i]'),
js('var cls = constructor.name'),
@@ -841,38 +851,7 @@ class CodeEmitterTask extends CompilerTask {
js('superConstructor ='
'existingIsolateProperties[superclass]')),
- js('var prototype = constructor.prototype'),
-
- // if ($supportsProtoName) {
- js.if_(supportsProtoName, [
- js('prototype.__proto__ = superConstructor.prototype'),
- js('prototype.constructor = constructor'),
-
- ], /* else */ [
- // function tmp() {};
- new jsAst.FunctionDeclaration(
- new jsAst.VariableDeclaration('tmp'),
- js.fun([], [])),
-
- js('tmp.prototype = superConstructor.prototype'),
- js('var newPrototype = new tmp()'),
-
- js('constructor.prototype = newPrototype'),
- js('newPrototype.constructor = constructor'),
-
- // for (var member in prototype) {
- js.forIn('member', 'prototype', [
- /* Short version of: if (member == '') */
- // if (!member) continue;
- js.if_('!member', new jsAst.Continue(null)),
-
- // if (hasOwnProperty.call(prototype, member)) {
- js.if_('hasOwnProperty.call(prototype, member)', [
- js('newPrototype[member] = prototype[member]')
- ])
- ])
-
- ])
+ js('prototype = inheritFrom(constructor, superConstructor)'),
]);
return new jsAst.FunctionDeclaration(
@@ -880,7 +859,7 @@ class CodeEmitterTask extends CompilerTask {
fun);
}
- jsAst.Fun get finishIsolateConstructorFunction {
+ jsAst.Fun get finishIsolateConstructorFunction_NO_CSP {
ngeoffray 2013/09/24 08:15:29 Is that dead code?
ahe 2013/09/24 14:05:09 Yes, removed in https://codereview.chromium.org/24
String isolate = namer.isolateName;
// We replace the old Isolate function with a new one that initializes
// all its field with the initial (and often final) value of all globals.
@@ -944,14 +923,45 @@ class CodeEmitterTask extends CompilerTask {
]));
}
+ jsAst.Fun get finishIsolateConstructorFunction {
+ // 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.
+ return js.fun('oldIsolate', [
+ js('var isolateProperties = oldIsolate.${namer.isolatePropertiesName}'),
+ new jsAst.FunctionDeclaration(
+ new jsAst.VariableDeclaration('Isolate'),
+ js.fun([], [
+ js('var hasOwnProperty = Object.prototype.hasOwnProperty'),
+ js.forIn('staticName', 'isolateProperties',
+ js.if_('hasOwnProperty.call(isolateProperties, staticName)',
+ js('this[staticName] = isolateProperties[staticName]'))),
+ // 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.
+ new jsAst.FunctionDeclaration(
+ new jsAst.VariableDeclaration('ForceEfficientMap'),
+ js.fun([], [])),
+ js('ForceEfficientMap.prototype = this'),
+ js('new ForceEfficientMap()')])),
+ js('Isolate.prototype = oldIsolate.prototype'),
+ js('Isolate.prototype.constructor = Isolate'),
+ js('Isolate.${namer.isolatePropertiesName} = isolateProperties'),
+ optional(needsDefineClass,
+ js('Isolate.$finishClassesProperty ='
+ ' oldIsolate.$finishClassesProperty')),
+ optional(hasMakeConstantList,
+ js('Isolate.makeConstantList = oldIsolate.makeConstantList')),
+ js.return_('Isolate')]);
+ }
+
jsAst.Fun get lazyInitializerFunction {
// function(prototype, staticName, fieldName, getterName, lazyValue) {
var parameters = <String>['prototype', 'staticName', 'fieldName',
'getterName', 'lazyValue'];
- return js.fun(parameters, [
- js('var getter = new Function("{ return this." + fieldName + ";}")'),
- ]..addAll(addLazyInitializerLogic())
- );
+ return js.fun(parameters, addLazyInitializerLogic());
}
List addLazyInitializerLogic() {
@@ -1004,7 +1014,8 @@ class CodeEmitterTask extends CompilerTask {
js.return_('result')
], finallyPart: [
- js('$isolate[getterName] = getter')
+ js('$isolate[getterName] = #',
+ js.fun([], [js.return_('this[fieldName]')]))
])
]))
]);
@@ -1013,7 +1024,7 @@ class CodeEmitterTask extends CompilerTask {
List buildDefineClassAndFinishClassFunctionsIfNecessary() {
if (!needsDefineClass) return [];
return defineClassFunction
- ..addAll(buildProtoSupportCheck())
+ ..addAll(buildInheritFrom())
..addAll([
js('$finishClassesName = #', finishClassesFunction)
]);
@@ -1709,29 +1720,34 @@ class CodeEmitterTask extends CompilerTask {
void generateGetter(Element member, String fieldName, String accessorName,
ClassBuilder builder) {
String getterName = namer.getterNameFromAccessorName(accessorName);
- String receiver = backend.isInterceptorClass(member.getEnclosingClass())
- ? 'receiver' : 'this';
- List<String> args = backend.isInterceptedMethod(member)
- ? ['receiver']
- : [];
- builder.addProperty(getterName,
- js.fun(args, js.return_(js('$receiver.$fieldName'))));
- generateReflectionDataForFieldGetterOrSetter(
- member, getterName, builder, isGetter: true);
+ ClassElement cls = member.getEnclosingClass();
+ String className = namer.getNameOfClass(cls);
+ String receiver = backend.isInterceptorClass(cls) ? 'receiver' : 'this';
+ List<String> args = backend.isInterceptedMethod(member) ? ['receiver'] : [];
+ precompiledFunction.add(
+ js('$className.prototype.$getterName = #',
+ js.fun(args, js.return_(js('$receiver.$fieldName')))));
+ if (backend.isNeededForReflection(member)) {
+ precompiledFunction.add(
+ js('$className.prototype.$getterName.${namer.reflectableField} = 1'));
+ }
}
void generateSetter(Element member, String fieldName, String accessorName,
ClassBuilder builder) {
String setterName = namer.setterNameFromAccessorName(accessorName);
- String receiver = backend.isInterceptorClass(member.getEnclosingClass())
- ? 'receiver' : 'this';
- List<String> args = backend.isInterceptedMethod(member)
- ? ['receiver', 'v']
- : ['v'];
- builder.addProperty(setterName,
- js.fun(args, js('$receiver.$fieldName = v')));
- generateReflectionDataForFieldGetterOrSetter(
- member, setterName, builder, isGetter: false);
+ ClassElement cls = member.getEnclosingClass();
+ String className = namer.getNameOfClass(cls);
+ String receiver = backend.isInterceptorClass(cls) ? 'receiver' : 'this';
+ List<String> args =
+ backend.isInterceptedMethod(member) ? ['receiver', 'v'] : ['v'];
+ precompiledFunction.add(
+ js('$className.prototype.$setterName = #',
+ js.fun(args, js.return_(js('$receiver.$fieldName = v')))));
+ if (backend.isNeededForReflection(member)) {
+ precompiledFunction.add(
+ js('$className.prototype.$setterName.${namer.reflectableField} = 1'));
+ }
}
bool canGenerateCheckedSetter(VariableElement field) {
@@ -1781,8 +1797,52 @@ class CodeEmitterTask extends CompilerTask {
member, setterName, builder, isGetter: false);
}
- void emitClassConstructor(ClassElement classElement, ClassBuilder builder) {
- /* Do nothing. */
+ void emitClassConstructor(ClassElement classElement,
+ ClassBuilder builder,
+ String runtimeName) {
+ List<String> fields = <String>[];
+ if (!classElement.isNative()) {
+ visitFields(classElement, false,
+ (Element member,
+ String name,
+ String accessorName,
+ bool needsGetter,
+ bool needsSetter,
+ bool needsCheckedSetter) {
+ fields.add(name);
+ });
+ }
+ String constructorName = namer.getNameOfClass(classElement);
+ precompiledFunction.add(new jsAst.FunctionDeclaration(
+ new jsAst.VariableDeclaration(constructorName),
+ js.fun(fields, fields.map(
+ (name) => js('this.$name = $name')).toList())));
+ if (runtimeName == null) {
+ runtimeName = constructorName;
+ }
+ precompiledFunction.addAll([
+ js('$constructorName.builtin\$cls = "$runtimeName"'),
+ js.if_('!"name" in $constructorName',
+ js('$constructorName.name = "$constructorName"')),
+ js('\$desc=\$collectedClasses.$constructorName'),
+ js.if_('\$desc instanceof Array', js('\$desc = \$desc[1]')),
+ js('$constructorName.prototype = \$desc'),
+ ]);
+
+ precompiledConstructorNames.add(js(constructorName));
+ }
+
+ jsAst.FunctionDeclaration buildPrecompiledFunction() {
+ // TODO(ahe): Compute a hash code.
+ String name = 'dart_precompiled';
+
+ precompiledFunction.add(
+ js.return_(
+ new jsAst.ArrayInitializer.from(precompiledConstructorNames)));
+ precompiledFunction.insert(0, js(r'var $desc'));
+ return new jsAst.FunctionDeclaration(
+ new jsAst.VariableDeclaration(name),
+ js.fun([r'$collectedClasses'], precompiledFunction));
}
void emitSuper(String superName, ClassBuilder builder) {
@@ -1966,13 +2026,11 @@ class CodeEmitterTask extends CompilerTask {
assert(!needsSetter);
generateCheckedSetter(member, name, accessorName, builder);
}
- if (!getterAndSetterCanBeImplementedByFieldSpec) {
- if (needsGetter) {
- generateGetter(member, name, accessorName, builder);
- }
- if (needsSetter) {
- generateSetter(member, name, accessorName, builder);
- }
+ if (needsGetter) {
+ generateGetter(member, name, accessorName, builder);
+ }
+ if (needsSetter) {
+ generateSetter(member, name, accessorName, builder);
}
});
});
@@ -2007,7 +2065,7 @@ class CodeEmitterTask extends CompilerTask {
}
ClassBuilder builder = new ClassBuilder();
- emitClassConstructor(classElement, builder);
+ emitClassConstructor(classElement, builder, runtimeName);
emitSuper(superName, builder);
emitRuntimeName(runtimeName, builder);
emitFields(classElement, builder, superName, onlyForRti: onlyForRti);
@@ -2075,8 +2133,6 @@ class CodeEmitterTask extends CompilerTask {
}
}
- bool get getterAndSetterCanBeImplementedByFieldSpec => true;
-
/// If this is true then we can generate the noSuchMethod handlers at startup
/// time, instead of them being emitted as part of the Object class.
bool get generateTrivialNsmHandlers => true;
@@ -2493,6 +2549,21 @@ class CodeEmitterTask extends CompilerTask {
ClassBuilder builder) {
builder.addProperty('',
js.string("$superName;${fieldNames.join(',')}"));
+
+ List<String> fields = fieldNames;
+ String constructorName = mangledName;
+ precompiledFunction.add(new jsAst.FunctionDeclaration(
+ new jsAst.VariableDeclaration(constructorName),
+ js.fun(fields, fields.map(
+ (name) => js('this.$name = $name')).toList())));
+ precompiledFunction.addAll([
+ js('$constructorName.builtin\$cls = "$constructorName"'),
+ js('\$desc=\$collectedClasses.$constructorName'),
+ js.if_('\$desc instanceof Array', js('\$desc = \$desc[1]')),
+ js('$constructorName.prototype = \$desc'),
+ ]);
+
+ precompiledConstructorNames.add(js(constructorName));
}
/**
@@ -2792,14 +2863,10 @@ class CodeEmitterTask extends CompilerTask {
ConstantHandler handler = compiler.constantHandler;
List<Constant> constants = handler.getConstantsForEmission(
compareConstants);
- bool addedMakeConstantList = false;
for (Constant constant in constants) {
if (isConstantInlinedOrAlreadyEmitted(constant)) continue;
String name = namer.constantName(constant);
- if (!addedMakeConstantList && constant.isList()) {
- addedMakeConstantList = true;
- emitMakeConstantList(eagerBuffer);
- }
+ if (constant.isList()) emitMakeConstantListIfNotEmitted(eagerBuffer);
CodeBuffer buffer = bufferForConstant(constant, eagerBuffer);
jsAst.Expression init = js(
'${namer.globalObjectForConstant(constant)}.$name = #',
@@ -2834,9 +2901,12 @@ class CodeEmitterTask extends CompilerTask {
return namer.constantName(a).compareTo(namer.constantName(b));
}
- void emitMakeConstantList(CodeBuffer buffer) {
- buffer.write(namer.isolateName);
- buffer.write(r'''.makeConstantList = function(list) {
+ void emitMakeConstantListIfNotEmitted(CodeBuffer buffer) {
+ if (hasMakeConstantList) return;
+ hasMakeConstantList = true;
+ buffer
+ ..write(namer.isolateName)
+ ..write(r'''.makeConstantList = function(list) {
list.immutable$list = true;
list.fixed$length = true;
return list;
@@ -3757,7 +3827,7 @@ class CodeEmitterTask extends CompilerTask {
buffer.write('];$n');
}
- void emitConvertToFastObjectFunction() {
+ void emitConvertToFastObjectFunction_NO_CSP() {
ngeoffray 2013/09/24 08:15:29 Ditto
mainBuffer.add(r'''
function convertToFastObject(properties) {
function makeConstructor() {
@@ -3777,6 +3847,37 @@ function convertToFastObject(properties) {
''');
}
+ void emitConvertToFastObjectFunction() {
+ // Create an instance that uses 'properties' as prototype. This should make
+ // 'properties' a fast object.
+ mainBuffer.add(r'''function convertToFastObject(properties) {
+ function MyClass() {};
+ MyClass.prototype = properties;
+ new MyClass();
+''');
+ if (DEBUG_FAST_OBJECTS) {
+ ClassElement primitives =
+ compiler.findHelper(const SourceString('Primitives'));
+ FunctionElement printHelper =
+ compiler.lookupElementIn(
+ primitives, const SourceString('printString'));
+ String printHelperName = namer.isolateAccess(printHelper);
+ mainBuffer.add('''
+// The following only works on V8 when run with option "--allow-natives-syntax".
+if (typeof $printHelperName === "function") {
+ $printHelperName("Size of global object: "
+ + String(Object.getOwnPropertyNames(properties).length)
+ + ", fast properties " + %HasFastProperties(properties));
+}
+''');
+ }
+mainBuffer.add(r'''
+ return properties;
+}
+''');
+ }
+
+
String assembleProgram() {
measure(() {
// Compute the required type checks to know which classes need a
@@ -4074,6 +4175,8 @@ if (typeof $printHelperName === "function") {
}
emitMain(mainBuffer);
+ jsAst.FunctionDeclaration precompiledFunctionAst =
+ buildPrecompiledFunction();
emitInitFunction(mainBuffer);
if (!areAnyElementsDeferred) {
mainBuffer.add('})()$n');
@@ -4081,6 +4184,13 @@ if (typeof $printHelperName === "function") {
compiler.assembledCode = mainBuffer.getText();
outputSourceMap(compiler.assembledCode, '');
+ mainBuffer.write(
+ jsAst.prettyPrint(precompiledFunctionAst, compiler).getText());
+
+ compiler.outputProvider('precompiled', 'js')
+ ..add(mainBuffer.getText())
+ ..close();
+
emitDeferredCode();
});

Powered by Google App Engine
This is Rietveld 408576698