From bf7b4c12c33e810b2bef2f49b1a45db30656f99a Mon Sep 17 00:00:00 2001 From: "jkummerow@chromium.org" Date: Thu, 11 Sep 2014 09:58:58 +0000 Subject: [PATCH] Add a script that can generate simple test cases for user-exposed JS builtins (we won't check in any such tests; they can be generated on the fly when needed) R=mbarbella@chromium.org Review URL: https://codereview.chromium.org/554683002 git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@23862 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- tools/detect-builtins.js | 51 +++++++++++++ tools/generate-builtins-tests.py | 158 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 209 insertions(+) create mode 100644 tools/detect-builtins.js create mode 100755 tools/generate-builtins-tests.py diff --git a/tools/detect-builtins.js b/tools/detect-builtins.js new file mode 100644 index 0000000..2a476ba --- /dev/null +++ b/tools/detect-builtins.js @@ -0,0 +1,51 @@ +// Copyright 2014 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +(function(global) { + + var GetProperties = function(this_name, object) { + var result = {}; + try { + var names = Object.getOwnPropertyNames(object); + } catch(e) { + return; + } + for (var i = 0; i < names.length; ++i) { + var name = names[i]; + if (typeof object === "function") { + if (name === "length" || + name === "name" || + name === "arguments" || + name === "caller" || + name === "prototype") { + continue; + } + } + // Avoid endless recursion. + if (this_name === "prototype" && name === "constructor") continue; + // Could get this from the parent, but having it locally is easier. + var property = { "name": name }; + try { + var value = object[name]; + } catch(e) { + property.type = "getter"; + result[name] = property; + continue; + } + var type = typeof value; + property.type = type; + if (type === "function") { + property.length = value.length; + property.prototype = GetProperties("prototype", value.prototype); + } + property.properties = GetProperties(name, value); + result[name] = property; + } + return result; + }; + + var g = GetProperties("", global, ""); + print(JSON.stringify(g, undefined, 2)); + +})(this); // Must wrap in anonymous closure or it'll detect itself as builtin. diff --git a/tools/generate-builtins-tests.py b/tools/generate-builtins-tests.py new file mode 100755 index 0000000..4e6961d --- /dev/null +++ b/tools/generate-builtins-tests.py @@ -0,0 +1,158 @@ +#!/usr/bin/env python +# Copyright 2014 the V8 project authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import json +import optparse +import os +import random +import shutil +import subprocess +import sys + + +BLACKLIST = [ + # Skip special d8 functions. + "load", "os", "print", "read", "readline", "quit" +] + + +def GetRandomObject(): + return random.choice([ + "0", "1", "2.5", "0x1000", "\"string\"", "{foo: \"bar\"}", "[1, 2, 3]", + "function() { return 0; }" + ]) + + +g_var_index = 0 + + +def GetVars(result, num, first = []): + global g_var_index + variables = [] + for i in range(num): + variables.append("__v_%d" % g_var_index) + g_var_index += 1 + for var in variables: + result.append("var %s = %s;" % (var, GetRandomObject())) + return ", ".join(first + variables) + + +# Wraps |string| in try..catch. +def TryCatch(result, string, exception_behavior = ""): + result.append("try { %s } catch(e) { %s }" % (string, exception_behavior)) + + +def BuildTests(function, full_name, options): + assert function["type"] == "function" + global g_var_index + g_var_index = 0 + result = ["// AUTO-GENERATED BY tools/generate-builtins-tests.py.\n"] + result.append("// Function call test:") + length = function["length"] + TryCatch(result, "%s(%s);" % (full_name, GetVars(result, length))) + + if "prototype" in function: + proto = function["prototype"] + result.append("\n// Constructor test:") + TryCatch(result, + "var recv = new %s(%s);" % (full_name, GetVars(result, length)), + "var recv = new Object();") + + getters = [] + methods = [] + for prop in proto: + proto_property = proto[prop] + proto_property_type = proto_property["type"] + if proto_property_type == "getter": + getters.append(proto_property) + result.append("recv.__defineGetter__(\"%s\", " + "function() { return %s; });" % + (proto_property["name"], GetVars(result, 1))) + if proto_property_type == "number": + result.append("recv.__defineGetter__(\"%s\", " + "function() { return %s; });" % + (proto_property["name"], GetVars(result, 1))) + if proto_property_type == "function": + methods.append(proto_property) + if getters: + result.append("\n// Getter tests:") + for getter in getters: + result.append("print(recv.%s);" % getter["name"]) + if methods: + result.append("\n// Method tests:") + for method in methods: + args = GetVars(result, method["length"], ["recv"]) + call = "%s.prototype.%s.call(%s)" % (full_name, method["name"], args) + TryCatch(result, call) + + filename = os.path.join(options.outdir, "%s.js" % (full_name)) + with open(filename, "w") as f: + f.write("\n".join(result)) + f.write("\n") + + +def VisitObject(obj, path, options): + obj_type = obj["type"] + obj_name = "%s%s" % (path, obj["name"]) + if obj_type == "function": + BuildTests(obj, obj_name, options) + if "properties" in obj: + for prop_name in obj["properties"]: + prop = obj["properties"][prop_name] + VisitObject(prop, "%s." % (obj_name), options) + + +def ClearGeneratedFiles(options): + if os.path.exists(options.outdir): + shutil.rmtree(options.outdir) + + +def GenerateTests(options): + ClearGeneratedFiles(options) # Re-generate everything. + output = subprocess.check_output( + "%s %s" % (options.d8, options.script), shell=True).strip() + objects = json.loads(output) + + os.makedirs(options.outdir) + for obj_name in objects: + if obj_name in BLACKLIST: continue + obj = objects[obj_name] + VisitObject(obj, "", options) + + +def BuildOptions(): + result = optparse.OptionParser() + result.add_option("--d8", help="d8 binary to use", + default="out/ia32.release/d8") + result.add_option("--outdir", help="directory where to place generated tests", + default="test/mjsunit/builtins-gen") + result.add_option("--script", help="builtins detector script to run in d8", + default="tools/detect-builtins.js") + return result + + +def Main(): + parser = BuildOptions() + (options, args) = parser.parse_args() + if len(args) != 1 or args[0] == "help": + parser.print_help() + return 1 + action = args[0] + + if action == "generate": + GenerateTests(options) + return 0 + + if action == "clear": + ClearGeneratedFiles(options) + return 0 + + print("Unknown action: %s" % action) + parser.print_help() + return 1 + + +if __name__ == "__main__": + sys.exit(Main()) -- 2.7.4