Start migrating error message templates to the runtime.

Currently done with two templates, one used from native js, one from runtime.

R=verwaest@chromium.org

Review URL: https://codereview.chromium.org/1087633005

Cr-Commit-Position: refs/heads/master@{#27864}
This commit is contained in:
yangguo 2015-04-16 00:01:20 -07:00 committed by Commit bot
parent 0e703bd34c
commit a5ac029058
14 changed files with 210 additions and 16 deletions

View File

@ -201,6 +201,8 @@ action("js2c") {
inputs = [ "tools/jsmin.py" ]
sources = [
"src/macros.py",
"src/messages.h",
"src/runtime.js",
"src/v8natives.js",
"src/symbol.js",
@ -227,7 +229,6 @@ action("js2c") {
"src/mirror-debugger.js",
"src/liveedit-debugger.js",
"src/templates.js",
"src/macros.py",
]
outputs = [
@ -263,6 +264,7 @@ action("js2c_experimental") {
sources = [
"src/macros.py",
"src/messages.h",
"src/proxy.js",
"src/generator.js",
"src/harmony-array.js",

View File

@ -102,7 +102,7 @@ function SetConstructor(iterable) {
if (!IS_NULL_OR_UNDEFINED(iterable)) {
var adder = this.add;
if (!IS_SPEC_FUNCTION(adder)) {
throw MakeTypeError('property_not_function', ['add', this]);
throw MakeTypeError(kPropertyNotFunction, ['add', this]);
}
for (var value of iterable) {
@ -268,7 +268,7 @@ function MapConstructor(iterable) {
if (!IS_NULL_OR_UNDEFINED(iterable)) {
var adder = this.set;
if (!IS_SPEC_FUNCTION(adder)) {
throw MakeTypeError('property_not_function', ['set', this]);
throw MakeTypeError(kPropertyNotFunction, ['set', this]);
}
for (var nextItem of iterable) {

View File

@ -1080,6 +1080,13 @@ Handle<Object> Factory::NewTypeError(const char* message,
}
Handle<Object> Factory::NewTypeError(MessageTemplate::Template template_index,
Handle<Object> arg0, Handle<Object> arg1,
Handle<Object> arg2) {
return NewError("MakeTypeError2", template_index, arg0, arg1, arg2);
}
Handle<Object> Factory::NewTypeError(Handle<String> message) {
return NewError("$TypeError", message);
}
@ -1138,6 +1145,39 @@ Handle<Object> Factory::NewError(const char* maker, const char* message,
}
Handle<Object> Factory::NewError(const char* maker,
MessageTemplate::Template template_index,
Handle<Object> arg0, Handle<Object> arg1,
Handle<Object> arg2) {
HandleScope scope(isolate());
Handle<String> error_maker = InternalizeUtf8String(maker);
Handle<Object> fun_obj = Object::GetProperty(isolate()->js_builtins_object(),
error_maker).ToHandleChecked();
Handle<JSFunction> fun = Handle<JSFunction>::cast(fun_obj);
Handle<Object> message_type(Smi::FromInt(template_index), isolate());
if (arg0.is_null()) arg0 = undefined_value();
if (arg1.is_null()) arg1 = undefined_value();
if (arg2.is_null()) arg2 = undefined_value();
Handle<Object> argv[] = {message_type, arg0, arg1, arg2};
// Invoke the JavaScript factory method. If an exception is thrown while
// running the factory method, use the exception as the result.
Handle<Object> result;
MaybeHandle<Object> exception;
if (!Execution::TryCall(fun, isolate()->js_builtins_object(), arraysize(argv),
argv, &exception).ToHandle(&result)) {
Handle<Object> exception_obj;
if (exception.ToHandle(&exception_obj)) {
result = exception_obj;
} else {
result = undefined_value();
}
}
return scope.CloseAndEscape(result);
}
Handle<Object> Factory::NewEvalError(const char* message,
Vector<Handle<Object> > args) {
return NewError("MakeEvalError", message, args);

View File

@ -6,6 +6,7 @@
#define V8_FACTORY_H_
#include "src/isolate.h"
#include "src/messages.h"
namespace v8 {
namespace internal {
@ -536,11 +537,20 @@ class Factory FINAL {
Handle<Object> NewError(const char* maker, const char* message,
Vector<Handle<Object> > args);
Handle<Object> NewError(const char* message, Vector<Handle<Object> > args);
Handle<Object> NewError(const char* maker,
MessageTemplate::Template template_index,
Handle<Object> arg0, Handle<Object> arg1,
Handle<Object> arg2);
Handle<Object> NewError(Handle<String> message);
Handle<Object> NewError(const char* constructor, Handle<String> message);
Handle<Object> NewTypeError(const char* message,
Vector<Handle<Object> > args);
Handle<Object> NewTypeError(MessageTemplate::Template template_index,
Handle<Object> arg0 = Handle<Object>(),
Handle<Object> arg1 = Handle<Object>(),
Handle<Object> arg2 = Handle<Object>());
Handle<Object> NewTypeError(Handle<String> message);
Handle<Object> NewRangeError(const char* message,

View File

@ -8,6 +8,7 @@
#include "src/execution.h"
#include "src/heap/spaces-inl.h"
#include "src/messages.h"
#include "src/string-builder.h"
namespace v8 {
namespace internal {
@ -162,4 +163,39 @@ SmartArrayPointer<char> MessageHandler::GetLocalizedMessage(
}
MaybeHandle<String> MessageTemplate::FormatMessage(int template_index,
Handle<String> arg0,
Handle<String> arg1,
Handle<String> arg2) {
const char* template_string;
switch (template_index) {
#define CASE(NAME, STRING) \
case k##NAME: \
template_string = STRING; \
break;
MESSAGE_TEMPLATES(CASE)
#undef CASE
case kLastMessage:
default:
UNREACHABLE();
template_string = "";
break;
}
Isolate* isolate = arg0->GetIsolate();
IncrementalStringBuilder builder(isolate);
int i = 0;
Handle<String> args[] = {arg0, arg1, arg2};
for (const char* c = template_string; *c != '\0'; c++) {
if (*c == '%') {
builder.AppendString(args[i++]);
DCHECK(i < arraysize(args));
} else {
builder.AppendCharacter(*c);
}
}
return builder.Finish();
}
} } // namespace v8::internal

View File

@ -10,8 +10,6 @@
#ifndef V8_MESSAGES_H_
#define V8_MESSAGES_H_
#include "src/handles-inl.h"
// Forward declaration of MessageLocation.
namespace v8 {
namespace internal {
@ -89,6 +87,25 @@ class MessageHandler {
Handle<Object> data);
};
#define MESSAGE_TEMPLATES(T) \
T(PropertyNotFunction, "Property '%' of object % is not a function") \
T(WithExpression, "% has no properties")
class MessageTemplate {
public:
enum Template {
#define TEMPLATE(NAME, STRING) k##NAME,
MESSAGE_TEMPLATES(TEMPLATE)
#undef TEMPLATE
kLastMessage
};
static MaybeHandle<String> FormatMessage(int template_index,
Handle<String> arg0,
Handle<String> arg1,
Handle<String> arg2);
};
} } // namespace v8::internal
#endif // V8_MESSAGES_H_

View File

@ -39,7 +39,6 @@ var kMessages = {
stack_trace: ["Stack Trace:\n", "%0"],
called_non_callable: ["%0", " is not a function"],
undefined_method: ["Object ", "%1", " has no method '", "%0", "'"],
property_not_function: ["Property '", "%0", "' of object ", "%1", " is not a function"],
cannot_convert_to_primitive: ["Cannot convert object to primitive value"],
not_constructor: ["%0", " is not a constructor"],
not_defined: ["%0", " is not defined"],
@ -47,7 +46,6 @@ var kMessages = {
unsupported_super: ["Unsupported reference to 'super'"],
non_object_property_load: ["Cannot read property '", "%0", "' of ", "%1"],
non_object_property_store: ["Cannot set property '", "%0", "' of ", "%1"],
with_expression: ["%0", " has no properties"],
illegal_invocation: ["Illegal invocation"],
no_setter_in_callback: ["Cannot set property ", "%0", " of ", "%1", " which has only a getter"],
apply_non_function: ["Function.prototype.apply was called on ", "%0", ", which is a ", "%1", " and not a function"],
@ -325,6 +323,11 @@ function MakeGenericError(constructor, type, args) {
}
function MakeGenericError2(constructor, type, arg0, arg1, arg2) {
return new constructor(FormatMessage(type, arg0, arg1, arg2));
}
/**
* Set up the Script function and constructor.
*/
@ -338,10 +341,21 @@ function MakeGenericError(constructor, type, args) {
// Helper functions; called from the runtime system.
function FormatMessage(type, args) {
function FormatMessage(type, arg0, arg1, arg2) {
if (IS_NUMBER(type)) {
var arg0 = NoSideEffectToString(arg0);
var arg1 = NoSideEffectToString(arg1);
var arg2 = NoSideEffectToString(arg2);
try {
return %FormatMessageString(type, arg0, arg1, arg2);
} catch (e) {
return "";
}
}
// TODO(yangguo): remove this code path once we migrated all messages.
var format = kMessages[type];
if (!format) return "<unknown message " + type + ">";
return FormatString(format, args);
return FormatString(format, arg0);
}
@ -371,6 +385,12 @@ function MakeTypeError(type, args) {
}
// TODO(yangguo): rename this once we migrated all messages.
function MakeTypeError2(type, arg0, arg1, arg2) {
return MakeGenericError2($TypeError, type, arg0, arg1, arg2);
}
function MakeRangeError(type, args) {
return MakeGenericError($RangeError, type, args);
}

View File

@ -305,6 +305,21 @@ RUNTIME_FUNCTION(Runtime_MessageGetScript) {
}
RUNTIME_FUNCTION(Runtime_FormatMessageString) {
HandleScope scope(isolate);
DCHECK(args.length() == 4);
CONVERT_INT32_ARG_CHECKED(template_index, 0);
CONVERT_ARG_HANDLE_CHECKED(String, arg0, 1);
CONVERT_ARG_HANDLE_CHECKED(String, arg1, 2);
CONVERT_ARG_HANDLE_CHECKED(String, arg2, 3);
Handle<String> result;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, result,
MessageTemplate::FormatMessage(template_index, arg0, arg1, arg2));
return *result;
}
RUNTIME_FUNCTION(Runtime_IS_VAR) {
UNREACHABLE(); // implemented as macro in the parser
return NULL;

View File

@ -7,6 +7,7 @@
#include "src/accessors.h"
#include "src/arguments.h"
#include "src/frames-inl.h"
#include "src/messages.h"
#include "src/runtime/runtime-utils.h"
#include "src/scopeinfo.h"
#include "src/scopes.h"
@ -662,7 +663,7 @@ RUNTIME_FUNCTION(Runtime_PushWithContext) {
if (!maybe_object.ToHandle(&extension_object)) {
Handle<Object> handle = args.at<Object>(0);
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError("with_expression", HandleVector(&handle, 1)));
isolate, NewTypeError(MessageTemplate::kWithExpression, handle));
}
}

View File

@ -287,6 +287,7 @@ namespace internal {
F(GetFromCacheRT, 2, 1) \
F(MessageGetStartPosition, 1, 1) \
F(MessageGetScript, 1, 1) \
F(FormatMessageString, 4, 1) \
F(IS_VAR, 1, 1) \
F(GetFromCache, 2, 1) \
F(IncrementStatsCounter, 1, 1) \

View File

@ -25,7 +25,7 @@ function WeakMapConstructor(iterable) {
if (!IS_NULL_OR_UNDEFINED(iterable)) {
var adder = this.set;
if (!IS_SPEC_FUNCTION(adder)) {
throw MakeTypeError('property_not_function', ['set', this]);
throw MakeTypeError(kPropertyNotFunction, ['set', this]);
}
for (var nextItem of iterable) {
if (!IS_SPEC_OBJECT(nextItem)) {
@ -116,7 +116,7 @@ function WeakSetConstructor(iterable) {
if (!IS_NULL_OR_UNDEFINED(iterable)) {
var adder = this.add;
if (!IS_SPEC_FUNCTION(adder)) {
throw MakeTypeError('property_not_function', ['add', this]);
throw MakeTypeError(kPropertyNotFunction, ['add', this]);
}
for (var value of iterable) {
%_CallFunction(this, value, adder);

View File

@ -36,6 +36,7 @@
#include "src/api.h"
#include "src/factory.h"
#include "src/messages.h"
#include "src/objects.h"
#include "src/unicode-decoder.h"
#include "test/cctest/cctest.h"
@ -1459,3 +1460,20 @@ INVALID_STRING_TEST(NewStringFromUtf8, char)
INVALID_STRING_TEST(NewStringFromOneByte, uint8_t)
#undef INVALID_STRING_TEST
TEST(FormatMessage) {
CcTest::InitializeVM();
LocalContext context;
Isolate* isolate = CcTest::i_isolate();
HandleScope scope(isolate);
Handle<String> arg0 = isolate->factory()->NewStringFromAsciiChecked("arg0");
Handle<String> arg1 = isolate->factory()->NewStringFromAsciiChecked("arg1");
Handle<String> arg2 = isolate->factory()->NewStringFromAsciiChecked("arg2");
Handle<String> result =
MessageTemplate::FormatMessage(MessageTemplate::kPropertyNotFunction,
arg0, arg1, arg2).ToHandleChecked();
Handle<String> expected = isolate->factory()->NewStringFromAsciiChecked(
"Property 'arg0' of object arg1 is not a function");
CHECK(String::Equals(result, expected));
}

View File

@ -1686,6 +1686,8 @@
],
'variables': {
'library_files': [
'../../src/macros.py',
'../../src/messages.h',
'../../src/runtime.js',
'../../src/v8natives.js',
'../../src/symbol.js',
@ -1712,10 +1714,10 @@
'../../src/mirror-debugger.js',
'../../src/liveedit-debugger.js',
'../../src/templates.js',
'../../src/macros.py',
],
'experimental_library_files': [
'../../src/macros.py',
'../../src/messages.h',
'../../src/proxy.js',
'../../src/generator.js',
'../../src/harmony-array.js',

View File

@ -69,6 +69,7 @@ def ReadFile(filename):
EVAL_PATTERN = re.compile(r'\beval\s*\(')
WITH_PATTERN = re.compile(r'\bwith\s*\(')
INVALID_ERROR_MESSAGE_PATTERN = re.compile(r'Make\w*Error\((k\w+),')
def Validate(lines):
# Because of simplified context setup, eval and with is not
@ -77,7 +78,9 @@ def Validate(lines):
raise Error("Eval disallowed in natives.")
if WITH_PATTERN.search(lines):
raise Error("With statements disallowed in natives.")
invalid_error = INVALID_ERROR_MESSAGE_PATTERN.search(lines)
if invalid_error:
raise Error("Unknown error message template '%s'" % invalid_error.group(1))
# Pass lines through unchanged.
return lines
@ -188,6 +191,21 @@ def ReadMacros(lines):
raise Error("Illegal line: " + line)
return (constants, macros)
TEMPLATE_PATTERN = re.compile(r'^\s+T\(([a-zA-Z]+), ".+"\)')
def ReadMessageTemplates(lines):
templates = []
index = 0
for line in lines.split('\n'):
template_match = TEMPLATE_PATTERN.match(line)
if template_match:
name = "k%s" % template_match.group(1)
value = index
index = index + 1
templates.append((re.compile("\\b%s\\b" % name), value))
return templates
INLINE_MACRO_PATTERN = re.compile(r'macro\s+([a-zA-Z0-9_]+)\s*\(([^)]*)\)\s*\n')
INLINE_MACRO_END_PATTERN = re.compile(r'endmacro\s*\n')
@ -311,7 +329,7 @@ GET_SCRIPT_NAME_CASE = """\
"""
def BuildFilterChain(macro_filename):
def BuildFilterChain(macro_filename, message_template_file):
"""Build the chain of filter functions to be applied to the sources.
Args:
@ -327,6 +345,10 @@ def BuildFilterChain(macro_filename):
filter_chain.append(lambda l: ExpandConstants(l, consts))
filter_chain.append(lambda l: ExpandMacros(l, macros))
if message_template_file:
message_templates = ReadMessageTemplates(ReadFile(message_template_file))
filter_chain.append(lambda l: ExpandConstants(l, message_templates))
filter_chain.extend([
RemoveCommentsAndTrailingWhitespace,
ExpandInlineMacros,
@ -354,6 +376,9 @@ def IsDebuggerFile(filename):
def IsMacroFile(filename):
return filename.endswith("macros.py")
def IsMessageTemplateFile(filename):
return filename.endswith("messages.h")
def PrepareSources(source_files):
"""Read, prepare and assemble the list of source files.
@ -372,7 +397,14 @@ def PrepareSources(source_files):
source_files.remove(macro_files[0])
macro_file = macro_files[0]
filters = BuildFilterChain(macro_file)
message_template_file = None
message_template_files = filter(IsMessageTemplateFile, source_files)
assert len(message_template_files) in [0, 1]
if message_template_files:
source_files.remove(message_template_files[0])
message_template_file = message_template_files[0]
filters = BuildFilterChain(macro_file, message_template_file)
# Sort 'debugger' sources first.
source_files = sorted(source_files,