Index: sdk/lib/js/dartium/js_dartium.dart |
diff --git a/sdk/lib/js/dartium/js_dartium.dart b/sdk/lib/js/dartium/js_dartium.dart |
index f184cfb04a7ef1fe21235c74eed2b8eb9b42d1d2..5f03fe1ac29bc52f58302b5c45146755b1f82280 100644 |
--- a/sdk/lib/js/dartium/js_dartium.dart |
+++ b/sdk/lib/js/dartium/js_dartium.dart |
@@ -138,6 +138,112 @@ final _jsInterfaceTypes = new Set<mirrors.ClassMirror>(); |
@Deprecated("Internal Use Only") |
Iterable<mirrors.ClassMirror> get jsInterfaceTypes => _jsInterfaceTypes; |
+class _StringLiteralEscape { |
+ // Character code constants. |
+ static const int BACKSPACE = 0x08; |
+ static const int TAB = 0x09; |
+ static const int NEWLINE = 0x0a; |
+ static const int CARRIAGE_RETURN = 0x0d; |
+ static const int FORM_FEED = 0x0c; |
+ static const int QUOTE = 0x22; |
+ static const int CHAR_$ = 0x24; |
+ static const int CHAR_0 = 0x30; |
+ static const int BACKSLASH = 0x5c; |
+ static const int CHAR_b = 0x62; |
+ static const int CHAR_f = 0x66; |
+ static const int CHAR_n = 0x6e; |
+ static const int CHAR_r = 0x72; |
+ static const int CHAR_t = 0x74; |
+ static const int CHAR_u = 0x75; |
+ |
+ final StringSink _sink; |
+ |
+ _StringLiteralEscape(this._sink); |
+ |
+ void writeString(String string) { |
+ _sink.write(string); |
+ } |
+ |
+ void writeStringSlice(String string, int start, int end) { |
+ _sink.write(string.substring(start, end)); |
+ } |
+ |
+ void writeCharCode(int charCode) { |
+ _sink.writeCharCode(charCode); |
+ } |
+ |
+ /// ('0' + x) or ('a' + x - 10) |
+ static int hexDigit(int x) => x < 10 ? 48 + x : 87 + x; |
+ |
+ /// Write, and suitably escape, a string's content as a JSON string literal. |
+ void writeStringContent(String s) { |
+ // Identical to JSON string lieral escaping except that we also escape $. |
sra1
2016/07/22 20:16:32
lieral?
Jacob
2016/07/25 16:31:58
Done.
|
+ int offset = 0; |
+ final int length = s.length; |
+ for (int i = 0; i < length; i++) { |
+ int charCode = s.codeUnitAt(i); |
+ if (charCode > BACKSLASH) continue; |
+ if (charCode < 32) { |
+ if (i > offset) writeStringSlice(s, offset, i); |
+ offset = i + 1; |
+ writeCharCode(BACKSLASH); |
+ switch (charCode) { |
+ case BACKSPACE: |
+ writeCharCode(CHAR_b); |
+ break; |
+ case TAB: |
+ writeCharCode(CHAR_t); |
+ break; |
+ case NEWLINE: |
+ writeCharCode(CHAR_n); |
+ break; |
+ case FORM_FEED: |
+ writeCharCode(CHAR_f); |
+ break; |
+ case CARRIAGE_RETURN: |
+ writeCharCode(CHAR_r); |
+ break; |
+ default: |
+ writeCharCode(CHAR_u); |
+ writeCharCode(CHAR_0); |
+ writeCharCode(CHAR_0); |
+ writeCharCode(hexDigit((charCode >> 4) & 0xf)); |
+ writeCharCode(hexDigit(charCode & 0xf)); |
+ break; |
+ } |
+ } else if (charCode == QUOTE || charCode == BACKSLASH || charCode == CHAR_$) { |
sra1
2016/07/22 20:16:32
line length
Jacob
2016/07/25 16:31:58
done. ran dartfmt
|
+ if (i > offset) writeStringSlice(s, offset, i); |
+ offset = i + 1; |
+ writeCharCode(BACKSLASH); |
+ writeCharCode(charCode); |
+ } |
+ } |
+ if (offset == 0) { |
+ writeString(s); |
+ } else if (offset < length) { |
+ writeStringSlice(s, offset, length); |
+ } |
+ } |
+ |
+ /** |
+ * Serialize a [num], [String], [bool], [Null], [List] or [Map] value. |
+ * |
+ * Returns true if the value is one of these types, and false if not. |
+ * If a value is both a [List] and a [Map], it's serialized as a [List]. |
sra1
2016/07/22 20:16:32
This comment is wrong.
|
+ */ |
+ bool writeStringLiteral(String str) { |
sra1
2016/07/22 20:16:32
Doesn't return anything.
Jacob
2016/07/25 16:31:58
Done.
|
+ writeString('"'); |
+ writeStringContent(str); |
+ writeString('"'); |
+ } |
+} |
+ |
+String _escapeString(String str) { |
+ StringBuffer output = new StringBuffer(); |
+ new _StringLiteralEscape(output)..writeStringLiteral(str); |
+ return output.toString(); |
sra1
2016/07/22 20:16:32
Could you use the JSON codec rather than copy & pa
Jacob
2016/07/25 16:31:58
Can't easily because of $
JSON doesn't need to esc
|
+} |
+ |
/// A collection of methods where all methods have the same name. |
/// This class is intended to optimize whether a specific invocation is |
/// appropritate for at least some of the methods in the collection. |
@@ -369,7 +475,7 @@ String _accessJsPathHelper(Iterable<String> parts) { |
..write('${_JS_LIBRARY_PREFIX}.JsNative.getProperty(' * parts.length) |
..write("${_JS_LIBRARY_PREFIX}.context"); |
for (var p in parts) { |
- sb.write(", '$p')"); |
+ sb.write(", ${_escapeString(p)})"); |
} |
return sb.toString(); |
} |
@@ -380,13 +486,13 @@ String _accessJsPathHelper(Iterable<String> parts) { |
String _accessJsPathSetter(String path) { |
var parts = path.split("."); |
return "${_JS_LIBRARY_PREFIX}.JsNative.setProperty(${_accessJsPathHelper(parts.getRange(0, parts.length - 1)) |
- }, '${parts.last}', v)"; |
+ }, ${_escapeString(parts.last)}, v)"; |
} |
String _accessJsPathCallMethodHelper(String path) { |
var parts = path.split("."); |
return "${_JS_LIBRARY_PREFIX}.JsNative.callMethod(${_accessJsPathHelper(parts.getRange(0, parts.length - 1)) |
- }, '${parts.last}',"; |
+ }, ${_escapeString(parts.last)},"; |
} |
@Deprecated("Internal Use Only") |
@@ -566,7 +672,7 @@ _generateLibraryCodegen(uri, library, staticCodegen) { |
mirrors.MirrorSystem.getName(p.simpleName)); |
sbPatch.write(" if($name != ${_UNDEFINED_VAR}) {\n" |
" ${_JS_LIBRARY_PREFIX}.safeForTypedInterop($name);\n" |
- " ${_JS_LIBRARY_PREFIX}.JsNative.setProperty(ret, '$jsName', $name);\n" |
+ " ${_JS_LIBRARY_PREFIX}.JsNative.setProperty(ret, ${_escapeString(jsName)}, $name);\n" |
" }\n"); |
i++; |
} |
@@ -1344,42 +1450,6 @@ class JsNative { |
static JSFunction withThis(Function f) native "JsFunction_withThisNoWrap"; |
} |
-/// Utility methods to efficiently manipulate typed JSInterop objects in cases |
-/// where the name to call is not known at runtime. You should only use these |
-/// methods when the same effect cannot be achieved with @JS annotations. |
-/// These methods would be extension methods on JSObject if Dart supported |
-/// extension methods. |
-class JSNative { |
- /** |
- * WARNING: performance of this method is much worse than other methods |
- * in JSNative. Only use this method as a last resort. |
- * |
- * Recursively converts a JSON-like collection of Dart objects to a |
- * collection of JavaScript objects and returns a [JsObject] proxy to it. |
- * |
- * [object] must be a [Map] or [Iterable], the contents of which are also |
- * converted. Maps and Iterables are copied to a new JavaScript object. |
- * Primitives and other transferrable values are directly converted to their |
- * JavaScript type, and all other objects are proxied. |
- */ |
- static jsify(object) { |
- if ((object is! Map) && (object is! Iterable)) { |
- throw new ArgumentError("object must be a Map or Iterable"); |
- } |
- return _jsify(object); |
- } |
- |
- static _jsify(object) native "JSObject_jsify"; |
- static JSObject newObject() native "JSObject_newObject"; |
- |
- static hasProperty(JSObject o, name) => o._hasProperty(name); |
- static getProperty(JSObject o, name) => o._operator_getter(name); |
- static setProperty(JSObject o, name, value) => o._operator_setter(name, value); |
- static callMethod(JSObject o, String method, List args) => o._callMethod(method, args); |
- static instanceof(JSObject o, Function type) => o._instanceof(type); |
- static callConstructor(JSObject constructor, List args) native "JSNative_callConstructor"; |
-} |
- |
/** |
* Proxies a JavaScript Function object. |
*/ |