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

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

Issue 11188004: Add a content-security-policy (CSP) flag. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Minor comment changes and rebase. Created 8 years, 2 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: lib/compiler/implementation/js_backend/emitter.dart
diff --git a/lib/compiler/implementation/js_backend/emitter.dart b/lib/compiler/implementation/js_backend/emitter.dart
index 82e80a80fa84e0598aef19aa8e8faeddc80addee..34a7714084830bb6bc7a806d6a38f1e4676703bc 100644
--- a/lib/compiler/implementation/js_backend/emitter.dart
+++ b/lib/compiler/implementation/js_backend/emitter.dart
@@ -46,14 +46,15 @@ class CodeEmitterTask extends CompilerTask {
Set<ClassElement> checkedClasses;
final bool generateSourceMap;
+ final bool useContentSecurityPolicy;
CodeEmitterTask(Compiler compiler, Namer namer,
- [bool generateSourceMap = false])
+ this.generateSourceMap,
+ this.useContentSecurityPolicy)
: boundClosureBuffer = new CodeBuffer(),
mainBuffer = new CodeBuffer(),
this.namer = namer,
boundClosureCache = new Map<int, String>(),
- generateSourceMap = generateSourceMap,
constantEmitter = new ConstantEmitter(compiler, namer),
super(compiler) {
nativeEmitter = new NativeEmitter(this);
@@ -98,6 +99,10 @@ class CodeEmitterTask extends CompilerTask {
final String GETTER_SETTER_SUFFIX = "=";
String get generateGetterSetterFunction {
+ if (useContentSecurityPolicy) {
+ compiler.internalError("Dynamic Getters/Setters are unused in CSP");
kasperl 2012/10/17 09:37:11 Getters/Setters -> getters/setters
floitsch 2012/10/17 21:15:01 Refactored.
+ }
+
return """
function(field, prototype) {
var len = field.length;
@@ -122,6 +127,13 @@ class CodeEmitterTask extends CompilerTask {
}
String get defineClassFunction {
+ if (useContentSecurityPolicy) {
kasperl 2012/10/17 09:37:11 It feels a little bit like subclassing would help
floitsch 2012/10/17 21:15:01 Done.
+ return """
+function(cls, constructor, prototype) {
+ constructor.prototype = prototype;
+ return constructor;
+}""";
+ }
// First the class name, then the super class name, followed by the fields
// (in an array) and the members (inside an Object literal).
// The caller can also pass in the constructor as a function if needed.
@@ -162,9 +174,16 @@ function(cls, fields, prototype) {
/** Needs defineClass to be defined. */
String get protoSupportCheck {
+ if (useContentSecurityPolicy) {
+ // We don't modify the prototypes in CSP mode. Therefore we can have an
+ // easier prototype-check.
+ return 'var $supportsProtoName = !!{}.__proto__;';
+ }
// 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.
@@ -242,6 +261,34 @@ function(collectedClasses) {
}
String get finishIsolateConstructorFunction {
+ if (useContentSecurityPolicy) {
+ // 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.
+ //
+ // We also copy over old values like the prototype, and the
+ // isolateProperties themselves.
+ return """
+function(oldIsolate) {
+ var isolateProperties = oldIsolate.${namer.ISOLATE_PROPERTIES};
kasperl 2012/10/17 09:37:11 Isn't there a way to construct the isolate object
floitsch 2012/10/17 21:15:01 There is, but since it's not necessary.
+ function Isolate() {
+ for (var staticName in isolateProperties) {
+ if (Object.prototype.hasOwnProperty.call(isolateProperties, staticName)) {
+ this[staticName] = isolateProperties[staticName];
+ }
+ }
+ // Use the newly created object as prototype. In Chrome this creates a
floitsch 2012/10/16 18:15:36 Thanks to Erik Corry for this tip. Without these l
+ // hidden class for the object and makes sure it is fast to access.
+ function t(){}
kasperl 2012/10/17 09:37:11 Maybe give t a better name? Space after ).
floitsch 2012/10/17 21:15:01 Done.
+ t.prototype = this;
+ new t();
+ }
+ Isolate.prototype = oldIsolate.prototype;
+ Isolate.prototype.constructor = Isolate;
+ Isolate.${namer.ISOLATE_PROPERTIES} = isolateProperties;
+ return Isolate;
+}""";
+ }
+
String isolate = namer.ISOLATE;
// 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.
@@ -620,6 +667,16 @@ function(prototype, staticName, fieldName, getterName, lazyValue) {
emitExtraAccessors(member, defineInstanceMember);
}
+ String generateGetter(Element member, String fieldName) {
+ String getterName = namer.getterName(member.getLibrary(), member.name);
+ return "$getterName: function() { return this.$fieldName; }";
+ }
+
+ String generateSetter(Element member, String fieldName) {
+ String setterName = namer.setterName(member.getLibrary(), member.name);
+ return "$setterName: function(v) { this.$fieldName = v; }";
+ }
+
String generateCheckedSetter(Element member, String fieldName) {
DartType type = member.computeType(compiler);
if (type.element.isTypeVariable()
@@ -635,7 +692,8 @@ function(prototype, staticName, fieldName, getterName, lazyValue) {
if (helperElement.computeSignature(compiler).parameterCount != 1) {
additionalArgument = ", '${namer.operatorIs(type.element)}'";
}
- return " set\$$fieldName: function(v) { "
+ String setterName = namer.setterName(member.getLibrary(), member.name);
+ return " $setterName: function(v) { "
"this.$fieldName = $helperName(v$additionalArgument); }";
}
}
@@ -645,16 +703,19 @@ function(prototype, staticName, fieldName, getterName, lazyValue) {
*
* Invariant: [classElement] must be a declaration element.
*/
- List<String> emitClassFields(ClassElement classElement, CodeBuffer buffer) {
+ void visitClassFields(ClassElement classElement,
+ void addField(Element member,
+ String name,
+ bool needsGetter,
+ bool needsSetter,
+ bool needsCheckedSetter)) {
assert(invariant(classElement, classElement.isDeclaration));
// If the class is never instantiated we still need to set it up for
// inheritance purposes, but we can simplify its JavaScript constructor.
bool isInstantiated =
compiler.codegenWorld.instantiatedClasses.contains(classElement);
- List<String> checkedSetters = <String>[];
- bool isFirstField = true;
- void addField(ClassElement enclosingClass, Element member) {
+ void visitField(ClassElement enclosingClass, Element member) {
assert(!member.isNative());
assert(invariant(classElement, member.isDeclaration));
@@ -665,48 +726,38 @@ function(prototype, staticName, fieldName, getterName, lazyValue) {
// We can only generate getters and setters for [classElement] since
// the fields of super classes could be overwritten with getters or
// setters.
- bool needsDynamicGetter = false;
- bool needsDynamicSetter = false;
+ bool needsGetter = false;
+ bool needsSetter = false;
// We need to name shadowed fields differently, so they don't clash with
// the non-shadowed field.
bool isShadowed = false;
if (enclosingClass === classElement) {
- needsDynamicGetter = instanceFieldNeedsGetter(member);
- needsDynamicSetter = instanceFieldNeedsSetter(member);
+ needsGetter = instanceFieldNeedsGetter(member);
+ needsSetter = instanceFieldNeedsSetter(member);
} else {
isShadowed = classElement.isShadowedByField(member);
}
if ((isInstantiated && !enclosingClass.isNative())
- || needsDynamicGetter
- || needsDynamicSetter) {
- if (isFirstField) {
- isFirstField = false;
- } else {
- buffer.add(", ");
- }
+ || needsGetter
+ || needsSetter) {
String fieldName = isShadowed
? namer.shadowedFieldName(member)
: namer.getName(member);
- if (needsDynamicSetter && compiler.enableTypeAssertions) {
+ bool needsCheckedSetter = false;
+ if (needsSetter && compiler.enableTypeAssertions) {
String setter = generateCheckedSetter(member, fieldName);
if (setter != null) {
- needsDynamicSetter = false;
- checkedSetters.add(setter);
+ needsSetter = false;
+ needsCheckedSetter = true;
}
}
// Getters and setters with suffixes will be generated dynamically.
- buffer.add('"$fieldName');
- if (needsDynamicGetter || needsDynamicSetter) {
- if (needsDynamicGetter && needsDynamicSetter) {
- buffer.add(GETTER_SETTER_SUFFIX);
- } else if (needsDynamicGetter) {
- buffer.add(GETTER_SUFFIX);
- } else {
- buffer.add(SETTER_SUFFIX);
- }
- }
- buffer.add('"');
+ addField(member,
+ fieldName,
+ needsGetter,
+ needsSetter,
+ needsCheckedSetter);
}
}
@@ -715,13 +766,100 @@ function(prototype, staticName, fieldName, getterName, lazyValue) {
// allowed on fields that are in [classElement] we don't need to visit
// superclasses for non-instantiated classes.
classElement.implementation.forEachInstanceField(
- addField,
+ visitField,
includeBackendMembers: true,
includeSuperMembers: isInstantiated && !classElement.isNative());
+ }
+
+ List<String> emitClassFields(ClassElement classElement, CodeBuffer buffer) {
+ buffer.add(' [');
+ bool isFirstField = true;
+ List<String> checkedSetters = <String>[];
+ visitClassFields(classElement, (Element member,
+ String name,
+ bool needsGetter,
+ bool needsSetter,
+ bool needsCheckedSetter) {
+ if (isFirstField) {
+ isFirstField = false;
+ } else {
+ buffer.add(", ");
+ }
+ if (needsCheckedSetter) {
+ assert(!needsSetter);
+ String setter = generateCheckedSetter(member, name);
+ if (setter !== null) {
+ needsSetter = null;
+ } else {
+ checkedSetters.add(setter);
+ }
+ }
+ buffer.add('"$name');
+ if (needsGetter && needsSetter) {
+ buffer.add(GETTER_SETTER_SUFFIX);
+ } else if (needsGetter) {
+ buffer.add(GETTER_SUFFIX);
+ } else if (needsSetter) {
+ buffer.add(SETTER_SUFFIX);
+ }
+ buffer.add('"');
+ });
+ buffer.add(']');
return checkedSetters;
}
/**
+ * This is the equivalent of [emitClassFields] when running under the
+ * Content-Security Policy.
+ */
+ List<String> emitClassConstructorGettersSetters(ClassElement classElement,
+ CodeBuffer buffer) {
+ // Say we have a class A with fields b, c and d, where c needs a getter and
+ // d needs both a getter and a setter. Then we produce:
+ // - a constructor (directly into the given [buffer]):
+ // function A(b, c, d) { this.b = b, this.c = c, this.d = d; }
+ // - getters and setters (stored in the [explicitGettersSetters] list):
+ // get$c : function() { return this.c; }
+ // get$d : function() { return this.d; }
+ // set$d : function(x) { this.d = x; }
+ List<String> explicitGettersSetters = <String>[];
+ List<String> fields = <String>[];
+ visitClassFields(classElement, (Element member,
+ String name,
+ bool needsGetter,
+ bool needsSetter,
+ bool needsCheckedSetter) {
+ fields.add(name);
+ if (needsCheckedSetter) {
+ assert(!needsSetter);
+ String setter = generateCheckedSetter(member, name);
+ if (setter !== null) {
+ needsSetter = null;
+ } else {
+ explicitGettersSetters.add(setter);
+ }
+ }
+ if (needsGetter) {
+ explicitGettersSetters.add(generateGetter(member, name));
+ }
+ if (needsSetter) {
+ explicitGettersSetters.add(generateSetter(member, name));
+ }
+ });
+
+ String constructorName = namer.safeName(classElement.name.slowToString());
+ // Generate the constructor.
+ buffer.add("function $constructorName(");
+ buffer.add(Strings.join(fields, ", "));
+ buffer.add(") {");
+ for (String field in fields) {
+ buffer.add(" this.$field = $field;");
+ }
+ buffer.add(' }');
+ return explicitGettersSetters;
+ }
+
+ /**
* Documentation wanted -- johnniwinther
*
* Invariant: [classElement] must be a declaration element.
@@ -796,19 +934,22 @@ function(prototype, staticName, fieldName, getterName, lazyValue) {
if (superclass !== null) {
superName = namer.getName(superclass);
}
- String constructorName = namer.safeName(classElement.name.slowToString());
buffer.add('$classesCollector.$className = {"":\n');
- buffer.add(' [');
- List<String> checkedSetters = emitClassFields(classElement, buffer);
- buffer.add('],\n');
+ List<String> explicitGettersSetters;
+ if (useContentSecurityPolicy) {
+ explicitGettersSetters =
+ emitClassConstructorGettersSetters(classElement, buffer);
+ } else {
+ explicitGettersSetters = emitClassFields(classElement, buffer);
+ }
// TODO(floitsch): the emitInstanceMember should simply always emit a ',\n'.
// That does currently not work because the native classes have a different
// syntax.
- buffer.add(' "super": "$superName"');
- if (!checkedSetters.isEmpty()) {
+ buffer.add(',\n "super": "$superName"');
+ if (!explicitGettersSetters.isEmpty()) {
buffer.add(',\n');
- buffer.add('${Strings.join(checkedSetters, ",\n")}');
+ buffer.add('${Strings.join(explicitGettersSetters, ",\n")}');
}
emitInstanceMembers(classElement, buffer, true);
buffer.add('\n};\n\n');
@@ -1020,11 +1161,19 @@ function(prototype, staticName, fieldName, getterName, lazyValue) {
// Define the constructor with a name so that Object.toString can
// find the class name of the closure class.
- boundClosureBuffer.add("""
+ if (useContentSecurityPolicy) {
+ boundClosureBuffer.add("""
+$classesCollector.$mangledName = {'':
+function $mangledName(self, target) { this.self = self; this.target = target; },
+ 'super': '$superName',
+""");
+ } else {
+ boundClosureBuffer.add("""
$classesCollector.$mangledName = {'':
['self', 'target'],
'super': '$superName',
""");
+ }
// Now add the methods on the closure class. The instance method does not
// have the correct name. Since [addParameterStubs] use the name to create
// its stubs we simply create a fake element with the correct name.

Powered by Google App Engine
This is Rietveld 408576698