| //------------------------------------------------------------------------------------------------------- | |
| // Copyright (C) Microsoft. All rights reserved. | |
| // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. | |
| //------------------------------------------------------------------------------------------------------- | |
| var echoFunctionString = echo.toString(); | |
| echo("// The tests in this file are GENERATED. Don't add tests to this file manually; instead, modify"); | |
| echo("// ArrayCheckHoist_Generate.js and regenerate this file, or use a different file for the new test."); | |
| echo(); | |
| var ArrayType = { | |
| ObjectWithArray: 0, | |
| Array: 1, | |
| LastJsArray: 1, | |
| Int32Array: 2 | |
| }; | |
| var JsArrayElementType = { | |
| Object: 0, | |
| Int32: 1, | |
| Float64: 2 | |
| }; | |
| var ArrayAccessType = { | |
| Local: 0, | |
| Field: 1 | |
| }; | |
| var ElementAccessType = { | |
| Load: 0, | |
| Store: 1 | |
| }; | |
| var NonInvariantLoopType = { | |
| None: 0, | |
| Redefine: 1, | |
| Call: 2, | |
| ImplicitCall: 3 | |
| }; | |
| var Indexes = [ | |
| "-1", | |
| "0", | |
| "i" | |
| ]; | |
| var Bools = [ | |
| false, | |
| true | |
| ]; | |
| // Test helper constants and variables | |
| var LoopIterations = 2; | |
| function ChangeToEs5ArrayStatement(a, jsArrayElementType) { | |
| var v = "-" + a + "[0]"; | |
| if(jsArrayElementType == JsArrayElementType.Object) | |
| v = "{ p: " + v + ".p - 1 }"; | |
| else | |
| v += " - 1"; | |
| return "Object.defineProperty(" + a + ", 0, { configurable: true, writable: false, enumerable: true, value: " + v + " });"; | |
| } | |
| var indent = 0; | |
| var testIndex = 0; | |
| echo("var bailout = !this.WScript || this.WScript.Arguments[0] === \"bailout\";"); | |
| echo(); | |
| var indexIndex = 0; | |
| var inlineIndex = 0; | |
| var strictIndex = 0; | |
| var indexChanger = 0; | |
| for(var arrayTypeIndex in ArrayType) { | |
| if(arrayTypeIndex.substring(0, 4) === "Last") | |
| continue; | |
| var arrayType = ArrayType[arrayTypeIndex]; | |
| for(var jsArrayElementTypeIndex in JsArrayElementType) { | |
| var jsArrayElementType = JsArrayElementType[jsArrayElementTypeIndex]; | |
| if(arrayType === ArrayType.Int32Array && jsArrayElementType !== JsArrayElementType.Int32) | |
| continue; | |
| for(var arrayAccessTypeIndex in ArrayAccessType) { | |
| var arrayAccessType = ArrayAccessType[arrayAccessTypeIndex]; | |
| for(var elementAccessTypeIndex in ElementAccessType) { | |
| var elementAccessType = ElementAccessType[elementAccessTypeIndex]; | |
| for(var nonInvariantLoopTypeIndex in NonInvariantLoopType) { | |
| var nonInvariantLoopType = NonInvariantLoopType[nonInvariantLoopTypeIndex]; | |
| for(var numLoops = 1; numLoops <= 4; numLoops *= 2) { | |
| for(var arrayAccessLoopIndex = 0; | |
| arrayAccessLoopIndex < numLoops; | |
| arrayAccessLoopIndex = (arrayAccessLoopIndex + 1) * 2 - 1) { | |
| var arrayAccess2LoopIndex = numLoops - 1 - arrayAccessLoopIndex; | |
| for(var nonInvariantLoopIndex = 0; | |
| nonInvariantLoopIndex < numLoops; | |
| nonInvariantLoopIndex = (nonInvariantLoopIndex + 1) * 2 - 1) { | |
| var index = Indexes[indexIndex % Indexes.length]; | |
| var inline = Bools[inlineIndex % Bools.length]; | |
| var strict = Bools[strictIndex % Bools.length]; | |
| ++indexChanger; | |
| if(!(indexChanger & 0x1))++indexIndex; | |
| if(!(indexChanger & 0x3))++inlineIndex; | |
| if(!(indexChanger & 0xf))++strictIndex; | |
| generate( | |
| arrayType, | |
| jsArrayElementType, | |
| arrayAccessType, | |
| elementAccessType, | |
| nonInvariantLoopType, | |
| numLoops, | |
| arrayAccessLoopIndex, | |
| arrayAccess2LoopIndex, | |
| nonInvariantLoopIndex, | |
| index, | |
| inline, | |
| strict); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| echo("////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////"); | |
| echo(); | |
| var f = ""; | |
| f += echos("function changeToEs5Array_object(a) {", 1); | |
| { | |
| f += preventInline(ChangeToEs5ArrayStatement("a", JsArrayElementType.Object)); | |
| } | |
| f += echos("}", -1); | |
| echo(f); | |
| f = ""; | |
| f += echos("function changeToEs5Array_int32(a) {", 1); | |
| { | |
| f += preventInline(ChangeToEs5ArrayStatement("a", JsArrayElementType.Int32)); | |
| } | |
| f += echos("}", -1); | |
| echo(f); | |
| f = ""; | |
| f += echos("function someCall(a) {", 1); | |
| { | |
| f += preventInline("a.someProperty = 0;"); | |
| } | |
| f += echos("}", -1); | |
| echo(f); | |
| echo(toSafeInt.toString()); | |
| echo(); | |
| echo(echoFunctionString); | |
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |
| // Test helpers | |
| function generate( | |
| arrayType, | |
| jsArrayElementType, | |
| arrayAccessType, | |
| elementAccessType, | |
| nonInvariantLoopType, | |
| numLoops, | |
| arrayAccessLoopIndex, | |
| arrayAccess2LoopIndex, | |
| nonInvariantLoopIndex, | |
| index, | |
| inline, | |
| strict) { | |
| if(numLoops === 0) | |
| throw new Error("Invalid numLoops"); | |
| if(arrayAccessLoopIndex < 0 || arrayAccessLoopIndex >= numLoops) | |
| throw new Error("Invalid arrayAccessLoopIndex"); | |
| if(arrayAccess2LoopIndex < 0 || arrayAccess2LoopIndex >= numLoops) | |
| throw new Error("Invalid arrayAccess2LoopIndex"); | |
| if(nonInvariantLoopIndex < 0 || nonInvariantLoopIndex >= numLoops) | |
| throw new Error("Invalid nonInvariantLoopIndex"); | |
| if(nonInvariantLoopType === NonInvariantLoopType.None && nonInvariantLoopIndex !== 0) | |
| return; // no non-invariant loops, only generate this test once | |
| if(arrayType >= ArrayType.LastJsArray && nonInvariantLoopType === NonInvariantLoopType.ImplicitCall) | |
| return; // typed arrays don't need implicit call checks, and their elements are not configurable | |
| if(strict && elementAccessType === ElementAccessType.Store && nonInvariantLoopType >= NonInvariantLoopType.Call) | |
| strict = false; // calls and implicit calls make elements read-only and those elements can't be set in strict mode thereafter | |
| var f = ""; | |
| var testName = "test" + testIndex++; | |
| f += echos("function " + testName + "() {", 1); | |
| { | |
| if(strict) { | |
| f += echos('"use strict";'); | |
| f += echos(""); | |
| } | |
| f += createArray(arrayType, jsArrayElementType, nonInvariantLoopType === NonInvariantLoopType.ImplicitCall); | |
| f += echos("return toSafeInt(" + testName + "_run(o, a, a2));"); | |
| f += echos(""); | |
| f += echos("function " + testName + "_run(o, a, a2) {", 1); | |
| { | |
| f += echos("var sum = 0;"); | |
| for(var i = 0; i < numLoops; ++i) { | |
| var si = "i" + i; | |
| var n = nonInvariantLoopType !== NonInvariantLoopType.None && i === nonInvariantLoopIndex ? LoopIterations : "a.length"; | |
| f += echos("for(var " + si + " = 0; " + si + " < " + n + "; ++" + si + ") {", 1); | |
| { | |
| f += echos("sum += " + si + ";"); | |
| } | |
| } | |
| for(var i = numLoops - 1; i >= 0; --i) { | |
| var si = "i" + i; | |
| if(nonInvariantLoopType !== NonInvariantLoopType.None && i === nonInvariantLoopIndex) { | |
| if(nonInvariantLoopType === NonInvariantLoopType.Redefine) | |
| f += echos("a = a2;"); | |
| else if(nonInvariantLoopType === NonInvariantLoopType.Call) { | |
| if(arrayType <= ArrayType.LastJsArray) | |
| f += echos("changeToEs5Array_" + (jsArrayElementType === JsArrayElementType.Object ? "object" : "int32") + "(a);"); | |
| else | |
| f += echos("someCall(a);"); | |
| } | |
| else if(nonInvariantLoopType === NonInvariantLoopType.ImplicitCall) { | |
| var n = nonInvariantLoopType !== NonInvariantLoopType.None && i === nonInvariantLoopIndex ? LoopIterations : "a.length"; | |
| f += echos("if(bailout && " + si + " === (" + n + " >> 1))", 1); | |
| { | |
| f += echos("o.changeToEs5Array = 0;"); | |
| } | |
| f += echos(null, -1); | |
| } | |
| else | |
| throw new Error("Invalid nonInvariantLoopType"); | |
| } | |
| if(i === arrayAccessLoopIndex || i === arrayAccess2LoopIndex) { | |
| if(inline) | |
| f += echos("sum += " + testName + "_access(o, a, " + si + ");"); | |
| else | |
| f += access(arrayType, jsArrayElementType, arrayAccessType, elementAccessType, index === "i" ? si : index); | |
| } | |
| f += echos("}", -1); | |
| } | |
| if(inline) { | |
| f += echos(""); | |
| f += echos("function " + testName + "_access(o, a, i) {", 1); | |
| { | |
| f += access(arrayType, jsArrayElementType, arrayAccessType, elementAccessType, index, true); | |
| } | |
| f += echos("}", -1); | |
| } | |
| f += echos("return sum;"); | |
| } | |
| f += echos("}", -1); | |
| } | |
| f += echos("}", -1); | |
| f += echos("echo(\"" + testName + ": \" + " + testName + "());"); | |
| echo(f); | |
| } | |
| function createArray(arrayType, jsArrayElementType, includeImplicitCall) { | |
| var f = ""; | |
| if(arrayType === ArrayType.Int32Array) { | |
| f += echos("var o = { a: new Int32Array(" + LoopIterations + ") };"); | |
| f += echos("for(var i = 0; i < " + LoopIterations + "; ++i)", 1); | |
| { | |
| f += echos("o.a[i] = i + 1;"); | |
| } | |
| f += echos(null, -1); | |
| } else { | |
| if(arrayType > ArrayType.LastJsArray) | |
| throw new Error("Unknown ArrayType"); | |
| f += echos("var o = {", 1); | |
| { | |
| var arrayBegin = arrayType === ArrayType.ObjectWithArray ? "{" : "["; | |
| var arrayEnd = arrayType === ArrayType.ObjectWithArray ? "}" : "]"; | |
| function arrayElementBegin(i) { | |
| return arrayType === ArrayType.ObjectWithArray ? "\"" + i + "\": " : ""; | |
| } | |
| var a = "a: " + arrayBegin + " "; | |
| for(var i = 1; i <= LoopIterations; ++i) { | |
| var end = i === LoopIterations ? "" : ", "; | |
| a += arrayElementBegin(i - 1); | |
| if(jsArrayElementType === JsArrayElementType.Object) | |
| a += "{ p: " + i + " }" + end; | |
| else if(jsArrayElementType === JsArrayElementType.Int32) | |
| a += i + end; | |
| else if(jsArrayElementType === JsArrayElementType.Float64) | |
| a += i + ".1" + end; | |
| else | |
| throw new Error("Unknown JsArrayElementType"); | |
| } | |
| if(arrayType === ArrayType.ObjectWithArray) | |
| a += ", length: " + LoopIterations; | |
| if(includeImplicitCall) { | |
| f += echos(a + " " + arrayEnd + ","); | |
| f += echos("set changeToEs5Array(v) {", 1); | |
| { | |
| f += preventInline(ChangeToEs5ArrayStatement("this.a", jsArrayElementType)); | |
| } | |
| f += echos("}", -1); | |
| } | |
| else | |
| f += echos(a + " " + arrayEnd); | |
| } | |
| f += echos("};", -1); | |
| } | |
| f += echos("var a = o.a;"); | |
| f += echos("a[-1] = a[0];"); | |
| if(arrayType === ArrayType.ObjectWithArray) | |
| f += echos("var a2 = [];"); | |
| else | |
| f += echos("var a2 = { length: a.length };"); | |
| f += echos("for(var i = -1; i < a.length; ++i)", 1); | |
| { | |
| if(arrayType <= ArrayType.LastJsArray && jsArrayElementType === JsArrayElementType.Object) | |
| f += echos("a2[i] = { p: -a[i].p };"); | |
| else | |
| f += echos("a2[i] = -a[i];"); | |
| } | |
| f += echos(null, -1); | |
| return f; | |
| } | |
| function access(arrayType, jsArrayElementType, arrayAccessType, elementAccessType, index, returnAccessed) { | |
| var f = ""; | |
| var e = "[" + index + "]"; | |
| if(arrayType <= ArrayType.LastJsArray && jsArrayElementType === JsArrayElementType.Object) | |
| e += ".p"; | |
| if(arrayAccessType === ArrayAccessType.Local) | |
| e = "a" + e; | |
| else if(arrayAccessType === ArrayAccessType.Field) | |
| e = "o.a" + e; | |
| else | |
| throw new Error("Unknown ArrayAccessType"); | |
| var beginning = returnAccessed ? "return " : "sum += "; | |
| if(elementAccessType === ElementAccessType.Load) | |
| f += echos(beginning + e + ";"); | |
| else if(elementAccessType === ElementAccessType.Store) | |
| f += echos(beginning + "(" + e + " = -" + e + " - 1, " + e + ");"); | |
| else | |
| throw new Error("Unknown ElementAccessType"); | |
| return f; | |
| } | |
| function preventInline(functionBodyLine) { | |
| var f = ""; | |
| f += echos("try {", 1); | |
| { | |
| f += echos(functionBodyLine); | |
| } | |
| f += echos("} catch(ex) {", -1); | |
| f += echos(null, 1); | |
| { | |
| f += echos('echo("Unexpected exception - " + ex.name + ": " + ex.message);'); | |
| } | |
| f += echos("}", -1); | |
| return f; | |
| } | |
| function echos(s, changeIndent) { | |
| if(changeIndent && changeIndent < 0) | |
| indent += changeIndent * 4; | |
| if(s && s !== "") { | |
| var spaces = ""; | |
| for(var i = 0; i < indent; ++i) | |
| spaces += " "; | |
| s = spaces + s + "\n"; | |
| } | |
| else if(s === "") | |
| s = "\n"; | |
| else | |
| s = ""; | |
| if(changeIndent && changeIndent > 0) | |
| indent += changeIndent * 4; | |
| return s; | |
| } | |
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |
| function toSafeInt(n) { | |
| return Math.round(Math.round(n * 10) / 10); | |
| } | |
| function echo() { | |
| var doEcho; | |
| if(this.WScript) | |
| doEcho = function(s) { this.WScript.Echo(s); }; | |
| else if(this.document) | |
| doEcho = function(s) { | |
| var div = this.document.createElement("div"); | |
| div.innerText = s; | |
| this.document.body.appendChild(div); | |
| }; | |
| else | |
| doEcho = function(s) { this.print(s); }; | |
| echo = function() { | |
| var s = ""; | |
| for(var i = 0; i < arguments.length; ++i) | |
| s += arguments[i]; | |
| doEcho(s); | |
| }; | |
| echo.apply(this, arguments); | |
| } |