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:
parent
0e703bd34c
commit
a5ac029058
4
BUILD.gn
4
BUILD.gn
@ -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",
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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_
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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) \
|
||||
|
@ -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);
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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',
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user