Revision: 5712
Author: [email protected]
Date: Wed Feb 25 21:23:55 2015 UTC
Log: Backport typed array whitelisting (5621,5650,5684,5685,5695,5710)
into es53 branch.
https://codereview.appspot.com/202280043
Typed array support in SES (r5621) had previously been excluded for the
simple reason that r5621 also includes changes to domado.js to use
typed arrays, but ES5/3 does not support typed arrays. In this change
we add the SES and taming membrane parts of typed array support only.
This change started as a merge
svn merge -c 5621,5684 ^/trunk
but contains additional edits, as well as manual backports of the
remainders of r5650, r5685, r5695, r5710 which did not previously
apply.
[email protected]
https://code.google.com/p/google-caja/source/detail?r=5712
Modified:
/branches/es53
/branches/es53/src/com/google/caja/plugin/taming-membrane.js
/branches/es53/src/com/google/caja/ses/repairES5.js
/branches/es53/src/com/google/caja/ses/startSES.js
/branches/es53/src/com/google/caja/ses/whitelist.js
/branches/es53/tests/com/google/caja/plugin/test-compare-modes.js
/branches/es53/tests/com/google/caja/plugin/test-scan-guest.js
/branches/es53/tests/com/google/caja/plugin/test-taming-inout-guest.js
=======================================
--- /branches/es53/src/com/google/caja/plugin/taming-membrane.js Tue Feb 24
19:34:47 2015 UTC
+++ /branches/es53/src/com/google/caja/plugin/taming-membrane.js Wed Feb 25
21:23:55 2015 UTC
@@ -15,7 +15,9 @@
/**
* Generic taming membrane implementation.
*
- * @requires WeakMap
+ * @requires WeakMap, ArrayBuffer, Int8Array, Uint8Array,
Uint8ClampedArray,
+ * Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array,
+ * Float64Array, DataView
* @overrides window
* @provides TamingMembrane
*/
@@ -96,11 +98,18 @@
}
}
- // Given a builtin object "o" provided by either a guest or host frame,
- // return a copy constructed in the taming frame. Return undefined if
- // "o" is not a builtin object. Note that we only call this function if
we
- // know that "o" is *not* a primitive.
- function copyBuiltin(o) {
+ /**
+ * Given a builtin object "o" from either side of the membrane, return a
copy
+ * constructed in the taming frame. Return undefined if "o" is not of a
type
+ * handled here. Note that we only call this function if we know
that "o" is
+ * *not* a primitive.
+ *
+ * This function handles only objects which we copy exactly once and
reuse
+ * the copy (via tamesTo()) if the same object is met again. For objects
which
+ * we copy every time they are passed across the membrane, see
+ * copyTreatedMutable below.
+ */
+ function copyTreatedImmutable(o) {
var t = void 0;
// Object.prototype.toString is spoofable (as of ES6). Therefore, each
// branch of this switch must assume that o is not necessarily of the
type
@@ -161,9 +170,78 @@
t.name = '' + name;
break;
}
+ break;
}
return t;
}
+
+ /**
+ * Helper function; return a copy of a typed array object without
depending on
+ * the typed array constructor to do it.
+ *
+ * This is needed, or at least reasonable caution, because typed array
+ * constructors have overloads that will share an ArrayBuffer with the
+ * provided value, rather than copying. In current specification and
+ * implementation it does not appear to be possible to create an object
which
+ * exploits this, but we don't wish to rely on that invariant.
+ */
+ function copyTArray(ctor, o) {
+ // Get a copy of the relevant portion of the underlying buffer in a way
+ // which has no overloads and guarantees a copy.
+ var byteOffset = +o.byteOffset;
+ var byteLength = +o.byteLength;
+ var buffer = ArrayBuffer.prototype.slice.call(
+ o.buffer, o.byteOffset, o.byteOffset + o.byteLength);
+
+ return new ctor(buffer);
+ }
+
+ /**
+ * Given a builtin object "o" from either side of the membrane, return a
copy
+ * constructed in the taming frame. Return undefined if "o" is not of a
type
+ * handled here. Note that we only call this function if we know
that "o" is
+ * *not* a primitive.
+ *
+ * This function handles only objects which should be copied every time
they
+ * are passed across the membrane. For objects which we wish to copy at
most
+ * once, see copyTreatedImmutable above.
+ */
+ function copyTreatedMutable(o, recursor) {
+ { // dummy block for minimum difference with trunk
+ var t = undefined;
+ // Object.prototype.toString is spoofable (as of ES6). Therefore,
each
+ // branch of this switch must assume that o is not necessarily of
the type
+ // and defend against that. However, we consider it acceptable for a
+ // spoofing object to be copied as one of what it was spoofing, or to
+ // cause an error.
+ switch (Object.prototype.toString.call(o)) {
+ // Note that these typed array tamings break any buffer sharing,
but
+ // that's in line with our general policy of copying.
+ //
+ // Note that we do not use privilegedAccess operations here, but
that
+ // is okay because ES5/3 does not support typed arrays and so this
code
+ // will never be reached.
+ case '[object ArrayBuffer]':
+ // ArrayBuffer.prototype.slice will always copy or throw
TypeError
+ t = ArrayBuffer.prototype.slice.call(o, 0);
+ break;
+ case '[object Int8Array]': t = copyTArray(Int8Array, o); break;
+ case '[object Uint8Array]': t = copyTArray(Uint8Array, o); break;
+ case '[object Uint8ClampedArray]':
+ t = copyTArray(Uint8ClampedArray, o); break;
+ case '[object Int16Array]': t = copyTArray(Int16Array, o); break;
+ case '[object Uint16Array]': t = copyTArray(Uint16Array, o);
break;
+ case '[object Int32Array]': t = copyTArray(Int32Array, o); break;
+ case '[object Uint32Array]': t = copyTArray(Uint32Array, o);
break;
+ case '[object Float32Array]': t = copyTArray(Float32Array, o);
break;
+ case '[object Float64Array]': t = copyTArray(Float64Array, o);
break;
+ case '[object DataView]':
+ t = new DataView(recursor(o.buffer), o.byteOffset, o.byteLength);
+ break;
+ }
+ return t;
+ }
+ }
// This is a last resort for passing a safe "demilitarized zone"
exception
// across the taming membrane in cases where passing the actual thrown
@@ -204,7 +282,7 @@
if ((f && tameByFeral.has(f)) || (t && feralByTame.has(t))) {
var et = tameByFeral.get(f);
var ef = feralByTame.get(t);
- throw new TypeError('Attempt to multiply tame: ' + f +
+ throw new TypeError('Attempt to multiply tame: ' + f +
(ef ? ' (already ' + (ef === f ? 'same' : ef) + ')' : '') +
' <-> ' + t +
(et ? ' (already ' + (et === t ? 'same' : et) + ')' : ''));
@@ -266,23 +344,25 @@
// Primitive value; tames to self
return f;
}
- var ftype = typeof f;
+ var t = tameByFeral.get(f);
+ if (t) { return t; }
if (Array.isArray(f)) {
// No tamesTo(...) for arrays; we copy across the membrane
return tameArray(f);
}
- var t = tameByFeral.get(f);
- if (t) { return t; }
if (feralByTame.has(f)) {
throw new TypeError('Tame object found on feral side of taming
membrane: '
+ f + '. The membrane has previously been compromised.');
}
+ var ftype = typeof f;
if (ftype === 'object') {
+ t = copyTreatedMutable(f, tame); // after type test to avoid toxic
fns
+ if (t) { return t; }
var ctor = privilegedAccess.directConstructor(f);
if (ctor === privilegedAccess.BASE_OBJECT_CONSTRUCTOR) {
t = preventExtensions(tameRecord(f));
} else {
- t = copyBuiltin(f);
+ t = copyTreatedImmutable(f);
if (t === void 0) {
if (ctor === void 0) {
throw new TypeError('Cannot determine ctor of: ' + f);
@@ -565,13 +645,10 @@
// Primitive value; untames to self
return t;
}
- var ttype = typeof t;
- if (Array.isArray(t)) {
- // No tamesTo(...) for arrays; we copy across the membrane
- return untameArray(t);
- }
var f = feralByTame.get(t);
if (f) { return f; }
+ f = copyTreatedMutable(t, untame);
+ if (f) { return f; }
if (tameByFeral.has(t)) {
throw new TypeError('Feral object found on tame side of taming
membrane: '
+ t + '. The membrane has previously been compromised.');
@@ -579,12 +656,17 @@
if (!privilegedAccess.isDefinedInCajaFrame(t)) {
throw new TypeError('Host object leaked without being tamed');
}
+ if (Array.isArray(t)) {
+ // No tamesTo(...) for arrays; we copy across the membrane
+ return untameArray(t);
+ }
+ var ttype = typeof t;
if (ttype === 'object') {
var ctor = privilegedAccess.directConstructor(t);
if (ctor === privilegedAccess.BASE_OBJECT_CONSTRUCTOR) {
f = untameCajaRecord(t);
} else {
- f = copyBuiltin(t);
+ f = copyTreatedImmutable(t);
if (f === void 0) {
throw new TypeError(
'Untaming of guest constructed objects unsupported: ' + t);
@@ -660,7 +742,7 @@
reTamesTo: reTamesTo,
hasTameTwin: hasTameTwin,
hasFeralTwin: hasFeralTwin,
-
+
// Any code which bypasses the membrane (e.g. in order to provide its
own
// tame twins, as Domado does) must also filter exceptions resulting
from
// control flow crossing the membrane.
=======================================
--- /branches/es53/src/com/google/caja/ses/repairES5.js Tue Feb 24 19:34:47
2015 UTC
+++ /branches/es53/src/com/google/caja/ses/repairES5.js Wed Feb 25 21:23:55
2015 UTC
@@ -36,8 +36,10 @@
*
* @author Mark S. Miller
* @requires ___global_test_function___, ___global_valueOf_function___
- * @requires JSON, navigator, this, eval, document
- * @overrides ses, RegExp, WeakMap, Object, parseInt, repairES5Module
+ * @requires JSON, eval, this
+ * @requires navigator, document, DOMException
+ * @overrides ses, repairES5Module
+ * @overrides RegExp, WeakMap, Object, parseInt
*/
var RegExp;
var ses;
@@ -818,6 +820,19 @@
// tests are rerun to see how they were effected by these repair
// attempts. Finally, we report what happened.
+ // Certain tests cannot operate without freezing primordial objects;
+ // they must therefore be run in separate frames with fresh
+ // primordials. Since the repairs will not have been performed in
+ // those frames, we use these flags to have the tests explicitly
+ // perform those repairs.
+ //
+ // TODO(kpreid): Figure out a better design for solving this problem.
+ // For example, it would be good to generically run the relevant tests
+ // after startSES has frozen everything and abort otherwise (this is
+ // done as a special case for FREEZING_BREAKS_PROTOTYPES only).
+ var repair_FREEZING_BREAKS_PROTOTYPES_wasApplied = false;
+ var repair_TYPED_ARRAY_PROTOS_LOOK_UNFROZEN_wasApplied = false;
+
/**
* If {@code Object.getOwnPropertyNames} is missing, we consider
* this to be an ES3 browser which is unsuitable for attempting to
@@ -2811,6 +2826,95 @@
delete ses.CANT_SAFELY_VERIFY_SYNTAX_canary;
}
}
+
+ var typedArrayNames = [
+ 'Int8Array',
+ 'Uint8Array',
+ 'Uint8ClampedArray',
+ 'Int16Array',
+ 'Uint16Array',
+ 'Int32Array',
+ 'Uint32Array',
+ 'Float32Array',
+ 'Float64Array'
+ ];
+
+ function test_TYPED_ARRAYS_THROW_DOMEXCEPTION() {
+ if (global.DataView === undefined) { return false; }
+ if (global.DOMException === undefined) { return false; }
+ function subtest(f) {
+ try {
+ f();
+ } catch (e) {
+ if (e instanceof DOMException) {
+ return true;
+ } else if (e instanceof Error && !(e instanceof DOMException)) {
+ return false;
+ } else {
+ return 'Exception from ' + f + ' of unexpected type: ' + e;
+ }
+ }
+ return f + ' did not throw';
+ };
+ return [
+ function() { new global.Int8Array(1).set(new global.Int8Array(),
10); },
+ function() { new global.DataView(new
global.ArrayBuffer(1)).getInt8(-1); }
+ ].some(subtest);
+ }
+
+ /**
+ * Observed on Safari 6.0.5 (8536.30.1): frozen typed array prototypes
report
+ * their properties as writable.
+ */
+ function test_TYPED_ARRAY_PROTOS_LOOK_UNFROZEN() {
+ // note: cannot test without frames
+ return inTestFrame(function(window) {
+ // Apply the repair which should fix the problem to the testing
frame.
+ // TODO(kpreid): Design a better architecture to handle cases like
this
+ // than one-off state flags.
+ if (repair_TYPED_ARRAY_PROTOS_LOOK_UNFROZEN_wasApplied) {
+ // optional argument not supplied by normal repair process
+ repair_TYPED_ARRAY_PROTOS_LOOK_UNFROZEN(window);
+ }
+
+ var fail = false;
+ typedArrayNames.forEach(function(ctorName) {
+ var ctor = window[ctorName];
+ if (!ctor) { return; }
+ var proto = ctor.prototype;
+
+ window.Object.freeze(proto);
+ if (!window.Object.isFrozen(proto)) {
+ fail = true;
+ return;
+ }
+
+ window.Object.getOwnPropertyNames(proto, function(prop) {
+ if (typeof fail === 'string') { return; }
+
+ // check attributes
+ var desc = window.Object.getOwnPropertyDescriptor(proto, prop);
+ if (!desc.configurable && desc.writable) {
+ fail = true;
+ } else if (!desc.configurable && !desc.writable) {
+ // correct result
+ } else {
+ fail = 'Unexpected property attributes for ' + ctorName + '.' +
+ prop;
+ return;
+ }
+
+ // check actual writability
+ try { proto[prop] = 9; } catch (e) {}
+ if (proto[prop] !== desc.value) {
+ fail = 'Unexpected actual writability of ' + ctorName + '.' +
prop;
+ return;
+ }
+ });
+ });
+ return fail;
+ });
+ }
/**
* Detects
@@ -3307,8 +3411,6 @@
// error message is matched elsewhere (for tighter bounds on catch)
var NO_CREATE_NULL =
'Repaired Object.create can not support Object.create(null)';
- // flag used for the test-of-repair which cannot operate normally.
- var repair_FREEZING_BREAKS_PROTOTYPES_wasApplied = false;
// optional argument is used for the test-of-repair
function repair_FREEZING_BREAKS_PROTOTYPES(opt_Object) {
var baseObject = opt_Object || Object;
@@ -3421,6 +3523,69 @@
// No known repairs under these conditions
}
}
+
+ function repair_TYPED_ARRAYS_THROW_DOMEXCEPTION() {
+ var protos = typedArrayNames.map(
+ function(ctorName) { return global[ctorName].prototype; });
+ protos.push(global.DataView.prototype);
+ protos.forEach(function(proto) {
+ Object.getOwnPropertyNames(proto).forEach(function(prop) {
+ if (/^[gs]et/.test(prop)) {
+ var origMethod = proto[prop];
+ proto[prop] = function exceptionAdapterWrapper(var_args) {
+ try {
+ origMethod.apply(this, arguments);
+ } catch (e) {
+ if (e instanceof DOMException) {
+ throw new RangeError(e.message);
+ }
+ }
+ };
+ }
+ });
+ });
+ }
+
+ function repair_TYPED_ARRAY_PROTOS_LOOK_UNFROZEN(opt_global) {
+ var targetGlobal = opt_global || global;
+ var typedArrayProtos = targetGlobal.Object.freeze(typedArrayNames.map(
+ function(ctorName) { return targetGlobal[ctorName].prototype; }));
+
+ var isFrozen = targetGlobal.Object.isFrozen;
+ var getOwnPropertyDescriptor =
targetGlobal.Object.getOwnPropertyDescriptor;
+
+ Object.defineProperty(targetGlobal.Object, 'getOwnPropertyDescriptor',
{
+ configurable: true,
+ writable: true, // allow other repairs to stack on
+ value: function getOwnPropertyDescriptor_typedArrayPatch(object,
prop) {
+ var desc = getOwnPropertyDescriptor(object, prop);
+ if (desc && typedArrayProtos.indexOf(object) !== -1 &&
+ 'value' in desc && ses._primordialsHaveBeenFrozen) {
+ // If it is one of the typed array prototypes then it will have
been
+ // frozen by startSES.
+ desc.writable = false;
+ }
+ return desc;
+ }
+ });
+
+ Object.defineProperty(targetGlobal.Object, 'isFrozen', {
+ configurable: true,
+ writable: true, // allow other repairs to stack on
+ value: function isFrozen_typedArrayPatch(object) {
+ // If it is one of the typed array prototypes then it will have
been
+ // frozen by startSES.
+ var v = typedArrayProtos.indexOf(object) !== -1;
+ return isFrozen(object) || (v && ses._primordialsHaveBeenFrozen);
+ }
+ });
+
+ // isSealed does not need repair as it already gives the correct
answer.
+
+ if (targetGlobal === global) {
+ repair_TYPED_ARRAY_PROTOS_LOOK_UNFROZEN_wasApplied = true;
+ }
+ }
function repair_GLOBAL_LEAKS_FROM_ARRAY_METHODS() {
var object = Array.prototype;
@@ -4608,6 +4773,32 @@
sections: ['15.3.2.1'],
tests: []
},
+ {
+ id: 'TYPED_ARRAYS_THROW_DOMEXCEPTION',
+ description: 'Typed Array operations throw DOMException',
+ test: test_TYPED_ARRAYS_THROW_DOMEXCEPTION,
+ repair: repair_TYPED_ARRAYS_THROW_DOMEXCEPTION,
+ // indirectly unsafe: DOMException is poisonous to WeakMaps on FF,
so we
+ // choose not to expose it, and un-whitelisted types do not get
frozen by
+ // startSES and are therefore global mutable state.
+ preSeverity: severities.NOT_OCAP_SAFE,
+ canRepair: true,
+ urls: [], // TODO(kpreid): file bugs if appropriate
+ sections: ['13.2.3'],
+ tests: [] // hopefully will be in ES6 tests
+ },
+ {
+ id: 'TYPED_ARRAY_PROTOS_LOOK_UNFROZEN',
+ description: 'Typed Array prototypes look unfrozen',
+ test: test_TYPED_ARRAY_PROTOS_LOOK_UNFROZEN,
+ repair: repair_TYPED_ARRAY_PROTOS_LOOK_UNFROZEN,
+ preSeverity: severities.SAFE_SPEC_VIOLATION,
+ canRepair: true,
+ urls: [], // TODO(kpreid): file bugs if appropriate
+ // appears on Safari only
+ sections: ['15.2.3.9', '15.2.3.12'],
+ tests: [] // hopefully will be in ES6 tests
+ },
{
id: 'NESTED_STRICT_FUNCTIONS_LEAK',
description: 'Strict nested functions leak from block scope',
=======================================
--- /branches/es53/src/com/google/caja/ses/startSES.js Thu Dec 18 19:31:03
2014 UTC
+++ /branches/es53/src/com/google/caja/ses/startSES.js Wed Feb 25 21:23:55
2015 UTC
@@ -1707,6 +1707,11 @@
// can skip it for non-defensive frames that must only be confined.
cajaVM.def(sharedImports);
+ // Internal communication back to repairES5 repairs that need to know if
+ // things have been frozen. TODO(kpreid): Consider making this more
specific
+ // (identifying the actually frozen objects) if that doesn't cost too
much.
+ ses._primordialsHaveBeenFrozen = true;
+
// The following objects are ambiently available via language
constructs, and
// therefore if we did not clean and defend them we have a problem. This
is
// defense against mistakes in modifying the whitelist, not against
browser
=======================================
--- /branches/es53/src/com/google/caja/ses/whitelist.js Tue Feb 24 19:34:47
2015 UTC
+++ /branches/es53/src/com/google/caja/ses/whitelist.js Wed Feb 25 21:23:55
2015 UTC
@@ -102,6 +102,7 @@
if (!ses) { ses = {}; }
var t = true;
+ var TypedArrayWhitelist; // defined and used below
ses.whitelist = {
cajaVM: { // Caja support
// This object is present here only to make it itself processed by
the
@@ -449,6 +450,62 @@
JSON: {
parse: t,
stringify: t
+ },
+ ArrayBuffer: { // Khronos Typed Arrays spec; ops are
safe
+ length: t, // does not inherit from Function.prototype on Chrome
+ name: t, // ditto
+ isView: t,
+ prototype: {
+ byteLength: 'maybeAccessor',
+ slice: t
+ }
+ },
+ Int8Array: TypedArrayWhitelist = { // Typed Arrays spec
+ length: t, // does not inherit from Function.prototype on Chrome
+ name: t, // ditto
+ BYTES_PER_ELEMENT: t,
+ prototype: {
+ buffer: 'maybeAccessor',
+ byteOffset: 'maybeAccessor',
+ byteLength: 'maybeAccessor',
+ length: 'maybeAccessor',
+ BYTES_PER_ELEMENT: t,
+ set: t,
+ subarray: t
+ }
+ },
+ Uint8Array: TypedArrayWhitelist,
+ Uint8ClampedArray: TypedArrayWhitelist,
+ Int16Array: TypedArrayWhitelist,
+ Uint16Array: TypedArrayWhitelist,
+ Int32Array: TypedArrayWhitelist,
+ Uint32Array: TypedArrayWhitelist,
+ Float32Array: TypedArrayWhitelist,
+ Float64Array: TypedArrayWhitelist,
+ DataView: { // Typed Arrays spec
+ length: t, // does not inherit from Function.prototype on Chrome
+ name: t, // ditto
+ prototype: {
+ buffer: 'maybeAccessor',
+ byteOffset: 'maybeAccessor',
+ byteLength: 'maybeAccessor',
+ getInt8: t,
+ getUint8: t,
+ getInt16: t,
+ getUint16: t,
+ getInt32: t,
+ getUint32: t,
+ getFloat32: t,
+ getFloat64: t,
+ setInt8: t,
+ setUint8: t,
+ setInt16: t,
+ setUint16: t,
+ setInt32: t,
+ setUint32: t,
+ setFloat32: t,
+ setFloat64: t
+ }
}
};
})();
=======================================
--- /branches/es53/tests/com/google/caja/plugin/test-compare-modes.js Wed
Aug 14 04:56:12 2013 UTC
+++ /branches/es53/tests/com/google/caja/plugin/test-compare-modes.js Wed
Feb 25 21:23:55 2015 UTC
@@ -225,6 +225,19 @@
parse: ON_ALL,
stringify: ON_ALL
},
+
+ // Features only supported on SES
+ ArrayBuffer: ON_SES,
+ DataView: ON_SES,
+ Float32Array: ON_SES,
+ Float64Array: ON_SES,
+ Int8Array: ON_SES,
+ Int16Array: ON_SES,
+ Int32Array: ON_SES,
+ Uint8Array: ON_SES,
+ Uint8ClampedArray: ON_SES,
+ Uint16Array: ON_SES,
+ Uint32Array: ON_SES,
cajaVM: {
// evaluation operations -- not supported in ES5/3
=======================================
--- /branches/es53/tests/com/google/caja/plugin/test-scan-guest.js Thu Dec
18 19:31:03 2014 UTC
+++ /branches/es53/tests/com/google/caja/plugin/test-scan-guest.js Wed Feb
25 21:23:55 2015 UTC
@@ -29,7 +29,8 @@
* HTMLTextAreaElement, HTMLVideoElement, HTMLButtonElement,
* Audio, Image, Option, XMLHttpRequest, Window, Document, Node,
Element,
* Attr, Text, CSSStyleDeclaration, CanvasRenderingContext2D,
- * CanvasGradient, ImageData, Location, TouchList
+ * CanvasGradient, ImageData, Location, TouchList,
+ * ArrayBuffer, Int8Array, DataView
* @overrides window
*/
@@ -351,6 +352,11 @@
var genAccessorSet = genMethod(G.value(cajaVM.def({
toString: function() { return '<setter garbage>'; }
})));
+ function genInstance(ctor) {
+ return G.lazyValue(function() {
+ return obtainInstance(ctor);
+ });
+ }
/** Add third value-callback to an arguments generator */
function annotate(calls, callback) {
return G.apply(function (call) {
@@ -1322,9 +1328,105 @@
obtainInstance.define(CanvasGradient,
document.createElement('canvas').getContext('2d').createLinearGradient(
0, 1, 2, 3));
- obtainInstance.define(ImageData,
- document.createElement('canvas').getContext('2d').createImageData(
- 2, 2));
+ obtainInstance.define(ImageData, (function() {
+ var v =
document.createElement('canvas').getContext('2d').createImageData(
+ 2, 2);
+ // v.data is an unfrozen native Uint8ClampedArray
+ expectedUnfrozen.setByIdentity(v.data);
+ expectedUnfrozen.setByIdentity(v.data.buffer);
+ return v;
+ }()));
+
+ // Combined Typed Array processing
+ // TODO(kpreid): Reorder everything so that args, obtainInstance, and
+ // expectedUnfrozen are done together for all types.
+ if (inES5Mode) (function() {
+
+ argsByAnyFrame('ArrayBuffer', genNew(genSmallInteger));
+ argsByAnyFrame('ArrayBuffer.isView', genMethod(G.any(
+ genObject, genInstance(Int8Array))));
+ argsByAnyFrame('ArrayBuffer.prototype.slice',
+ freshResult(genMethod(genSmallInteger, genSmallInteger)));
+ obtainInstance.define(ArrayBuffer, new ArrayBuffer(3));
+ expectedUnfrozen.setByConstructor(ArrayBuffer, true);
+ expectedUnfrozen.setByConstructor(
+ simpleEval(tamingEnv, 'ArrayBuffer'), true);
+
+ forEachFrame('Int8Array', function(arrayCtor) {
+ // inert ctor we need to note
+ var ArrayBufferView =
+ Object.getPrototypeOf(arrayCtor.prototype).constructor;
+ if (ArrayBufferView === Object) return;
+ var refArrayBufferView = Ref.is(ArrayBufferView);
+ functionArgs.set(refArrayBufferView, genNew());
+ expectedAlwaysThrow.mark(refArrayBufferView);
+ });
+
+ // PLAIN_CALL is needed on Firefox because it allows calling these
ctors
+ var typedArrayCall = G.tuple(G.value(CONSTRUCT, PLAIN_CALL), G.any(
+ G.tuple(genSmallInteger),
+ G.tuple(genInstance(Int8Array)),
+ G.tuple(genNumbers(2)),
+ genConcat(
+ G.tuple(genInstance(ArrayBuffer)),
+ genNumbers(2))));
+ function setupTypedArray(name, doAllCalls) {
+ var ref = RefAnyFrame(name);
+ var ctor = window[name];
+
+ functionArgs.set(ref, freshResult(doAllCalls
+ ? typedArrayCall
+ : genAllCall(G.value(0))));
+ obtainInstance.define(ctor, new ctor(3));
+
+ argsByAnyFrame(name + '.prototype.set', doAllCalls
+ ? G.any(
+ genMethod(genInstance(Int8Array), genSmallInteger),
+ genMethod(G.value([1, 2, 3]), genSmallInteger))
+ : genMethod(G.value([1, 2, 3]), G.value(0)));
+ argsByAnyFrame(name + '.prototype.subarray', freshResult(
+ doAllCalls
+ ? genMethod(genSmallInteger, genSmallInteger)
+ : genMethod(G.value(1), G.value(2))));
+ }
+ // To save on scan time, we only fully exercise some of the array
types
+ // (chosen for coverage of different cases: 1-byte, clamped,
endianness,
+ // floats).
+ setupTypedArray('Int8Array', true);
+ setupTypedArray('Uint8Array', false);
+ setupTypedArray('Uint8ClampedArray', true);
+ setupTypedArray('Int16Array', false);
+ setupTypedArray('Uint16Array', false);
+ setupTypedArray('Int32Array', false);
+ setupTypedArray('Uint32Array', true);
+ setupTypedArray('Float32Array', false);
+ setupTypedArray('Float64Array', true);
+
+ argsByAnyFrame('DataView', genNew(
+ genInstance(ArrayBuffer), genSmallInteger, genSmallInteger));
+ obtainInstance.define(DataView, new DataView(new ArrayBuffer(8)));
+ expectedUnfrozen.setByConstructor(DataView, true);
+ var get8 = genMethod(genSmallInteger);
+ argsByAnyFrame('DataView.prototype.getInt8', get8);
+ argsByAnyFrame('DataView.prototype.getUint8', get8);
+ var getWide = genMethod(genSmallInteger, genBoolean);
+ argsByAnyFrame('DataView.prototype.getInt16', getWide);
+ argsByAnyFrame('DataView.prototype.getUint16', getWide);
+ argsByAnyFrame('DataView.prototype.getInt32', getWide);
+ argsByAnyFrame('DataView.prototype.getUint32', getWide);
+ argsByAnyFrame('DataView.prototype.getFloat32', getWide);
+ argsByAnyFrame('DataView.prototype.getFloat64', getWide);
+ var set8 = genMethod(genSmallInteger, genNumber);
+ argsByAnyFrame('DataView.prototype.setInt8', set8);
+ argsByAnyFrame('DataView.prototype.setUint8', set8);
+ var setWide = genMethod(genSmallInteger, genNumber, genBoolean);
+ argsByAnyFrame('DataView.prototype.setInt16', setWide);
+ argsByAnyFrame('DataView.prototype.setUint16', setWide);
+ argsByAnyFrame('DataView.prototype.setInt32', setWide);
+ argsByAnyFrame('DataView.prototype.setUint32', setWide);
+ argsByAnyFrame('DataView.prototype.setFloat32', setWide);
+ argsByAnyFrame('DataView.prototype.setFloat64', setWide);
+ })();
var tamingFeralWin = directAccess.evalInTamingFrame('window');
var guestFeralWin = directAccess.evalInGuestFrame('window');
=======================================
--- /branches/es53/tests/com/google/caja/plugin/test-taming-inout-guest.js
Tue Feb 24 19:34:47 2015 UTC
+++ /branches/es53/tests/com/google/caja/plugin/test-taming-inout-guest.js
Wed Feb 25 21:23:55 2015 UTC
@@ -175,6 +175,57 @@
pass('testGuestConstructedObjectMethods');
});
+jsunitRegisterIf(
+ typeof ArrayBuffer !== 'undefined',
+ 'testGuestTypedArrays',
+ function testGuestTypedArrays() {
+ // Note: We haven't written a corresponding guest-to-host test, because
the
+ // handling of typed arrays is fully symmetric (copyUnmemoized) and has
no
+ // reason to behave differently.
+
+ var buf = new Uint8Array([1, 2, 3]).buffer;
+ tamedApi.tamedHostPureFunction(
+ 'assertTrue("value " + a, a instanceof frame.imports.ArrayBuffer);',
buf);
+ tamedApi.tamedHostPureFunction(
+ 'assertEquals("buffer length", 3, a.byteLength);', buf);
+ tamedApi.tamedHostPureFunction(
+ 'assertEquals("buffer element", 2, new Uint8Array(a)[1]);', buf);
+
+ // Testing array type and also specifically the lack of buffer sharing.
+ var a = new Uint8Array([1, 2, 3]);
+ var b = new Uint8Array(a.buffer);
+ tamedApi.tamedHostPureFunction(
+ 'assertTrue("array " + a, a instanceof frame.imports.Uint8Array);',
a, b);
+ tamedApi.tamedHostPureFunction(
+ 'assertNotEquals("array buffer", a.buffer, b.buffer);', a, b);
+ tamedApi.tamedHostPureFunction('a[0] = 99;', a);
+ // no persistent mutation and sharing
+ tamedApi.tamedHostPureFunction(
+ 'assertEquals("not mutated", 2, a[0] + b[0]);', a, b);
+ assertEquals('guest not mutated', 1, a[0]);
+
+ // DataView
+ var array = new Uint8Array([0, 0, 0, 1, 0, 1]);
+ var vbuf = array.buffer;
+ var view = new DataView(vbuf, 1, 4);
+ tamedApi.tamedHostPureFunction(
+ 'assertTrue("view: " + a, a instanceof frame.imports.DataView);',
+ view, vbuf);
+ tamedApi.tamedHostPureFunction(
+ 'assertNotEquals("equality", a.buffer, b);', view, vbuf);
+ tamedApi.tamedHostPureFunction('assertEquals("bo", 1, a.byteOffset);',
view);
+ tamedApi.tamedHostPureFunction('assertEquals("bl", 4, a.byteLength);',
view);
+ tamedApi.tamedHostPureFunction(
+ 'assertEquals("value", 256, a.getUint32(0));', view);
+ tamedApi.tamedHostPureFunction(
+ 'a.setUint32(0, 10);', view);
+ assertEquals('mutation check 1', 1, array[3]);
+ assertEquals('mutation check 2', 0, array[4]);
+
+ pass('testGuestTypedArrays');
+});
+
+
jsunitRegister('testMembraneViolation',
function testMembraneViolation() {
expectFailure(function() {
--
---
You received this message because you are subscribed to the Google Groups "Google Caja Discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/d/optout.