Commit a5ac0290 authored by yangguo's avatar yangguo Committed by Commit bot

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}
parent 0e703bd3
......@@ -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,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment