summaryrefslogtreecommitdiff
path: root/deps/v8/src/asmjs
diff options
context:
space:
mode:
authorMichaël Zasso <targos@protonmail.com>2017-06-06 10:28:14 +0200
committerMichaël Zasso <targos@protonmail.com>2017-06-07 10:33:31 +0200
commit3dc8c3bed4cf3a77607edbb0b015e33f8b60fc09 (patch)
tree9dee56e142638b34f1eccbd0ad88c3bce5377c29 /deps/v8/src/asmjs
parent91a1bbe3055a660194ca4d403795aa0c03e9d056 (diff)
downloadandroid-node-v8-3dc8c3bed4cf3a77607edbb0b015e33f8b60fc09.tar.gz
android-node-v8-3dc8c3bed4cf3a77607edbb0b015e33f8b60fc09.tar.bz2
android-node-v8-3dc8c3bed4cf3a77607edbb0b015e33f8b60fc09.zip
deps: update V8 to 5.9.211.32
PR-URL: https://github.com/nodejs/node/pull/13263 Reviewed-By: Gibson Fahnestock <gibfahn@gmail.com> Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Franziska Hinkelmann <franziska.hinkelmann@gmail.com> Reviewed-By: Myles Borins <myles.borins@gmail.com>
Diffstat (limited to 'deps/v8/src/asmjs')
-rw-r--r--deps/v8/src/asmjs/asm-js.cc138
-rw-r--r--deps/v8/src/asmjs/asm-names.h110
-rw-r--r--deps/v8/src/asmjs/asm-parser.cc2449
-rw-r--r--deps/v8/src/asmjs/asm-parser.h316
-rw-r--r--deps/v8/src/asmjs/asm-scanner.cc431
-rw-r--r--deps/v8/src/asmjs/asm-scanner.h165
-rw-r--r--deps/v8/src/asmjs/asm-wasm-builder.cc12
7 files changed, 3574 insertions, 47 deletions
diff --git a/deps/v8/src/asmjs/asm-js.cc b/deps/v8/src/asmjs/asm-js.cc
index 95d1e8a64f..fb46026a98 100644
--- a/deps/v8/src/asmjs/asm-js.cc
+++ b/deps/v8/src/asmjs/asm-js.cc
@@ -6,6 +6,7 @@
#include "src/api-natives.h"
#include "src/api.h"
+#include "src/asmjs/asm-parser.h"
#include "src/asmjs/asm-typer.h"
#include "src/asmjs/asm-wasm-builder.h"
#include "src/assert-scope.h"
@@ -164,47 +165,90 @@ bool IsStdlibMemberValid(i::Isolate* isolate, Handle<JSReceiver> stdlib,
} // namespace
MaybeHandle<FixedArray> AsmJs::CompileAsmViaWasm(CompilationInfo* info) {
- ErrorThrower thrower(info->isolate(), "Asm.js -> WebAssembly conversion");
+ wasm::ZoneBuffer* module = nullptr;
+ wasm::ZoneBuffer* asm_offsets = nullptr;
+ Handle<FixedArray> uses_array;
+ Handle<FixedArray> foreign_globals;
base::ElapsedTimer asm_wasm_timer;
asm_wasm_timer.Start();
wasm::AsmWasmBuilder builder(info);
- Handle<FixedArray> foreign_globals;
- auto asm_wasm_result = builder.Run(&foreign_globals);
- if (!asm_wasm_result.success) {
- DCHECK(!info->isolate()->has_pending_exception());
- if (!FLAG_suppress_asm_messages) {
- MessageHandler::ReportMessage(info->isolate(),
- builder.typer()->message_location(),
- builder.typer()->error_message());
+ if (FLAG_fast_validate_asm) {
+ wasm::AsmJsParser parser(info->isolate(), info->zone(), info->script(),
+ info->literal()->start_position(),
+ info->literal()->end_position());
+ if (!parser.Run()) {
+ DCHECK(!info->isolate()->has_pending_exception());
+ if (!FLAG_suppress_asm_messages) {
+ MessageLocation location(info->script(), parser.failure_location(),
+ parser.failure_location());
+ Handle<String> message =
+ info->isolate()
+ ->factory()
+ ->NewStringFromUtf8(CStrVector(parser.failure_message()))
+ .ToHandleChecked();
+ Handle<JSMessageObject> error_message =
+ MessageHandler::MakeMessageObject(
+ info->isolate(), MessageTemplate::kAsmJsInvalid, &location,
+ message, Handle<JSArray>::null());
+ error_message->set_error_level(v8::Isolate::kMessageWarning);
+ MessageHandler::ReportMessage(info->isolate(), &location,
+ error_message);
+ }
+ return MaybeHandle<FixedArray>();
+ }
+ Zone* zone = info->zone();
+ module = new (zone) wasm::ZoneBuffer(zone);
+ parser.module_builder()->WriteTo(*module);
+ asm_offsets = new (zone) wasm::ZoneBuffer(zone);
+ parser.module_builder()->WriteAsmJsOffsetTable(*asm_offsets);
+ // TODO(bradnelson): Remove foreign_globals plumbing (as we don't need it
+ // for the new parser).
+ foreign_globals = info->isolate()->factory()->NewFixedArray(0);
+ uses_array = info->isolate()->factory()->NewFixedArray(
+ static_cast<int>(parser.stdlib_uses()->size()));
+ int count = 0;
+ for (auto i : *parser.stdlib_uses()) {
+ uses_array->set(count++, Smi::FromInt(i));
+ }
+ } else {
+ auto asm_wasm_result = builder.Run(&foreign_globals);
+ if (!asm_wasm_result.success) {
+ DCHECK(!info->isolate()->has_pending_exception());
+ if (!FLAG_suppress_asm_messages) {
+ MessageHandler::ReportMessage(info->isolate(),
+ builder.typer()->message_location(),
+ builder.typer()->error_message());
+ }
+ return MaybeHandle<FixedArray>();
+ }
+ module = asm_wasm_result.module_bytes;
+ asm_offsets = asm_wasm_result.asm_offset_table;
+ wasm::AsmTyper::StdlibSet uses = builder.typer()->StdlibUses();
+ uses_array = info->isolate()->factory()->NewFixedArray(
+ static_cast<int>(uses.size()));
+ int count = 0;
+ for (auto i : uses) {
+ uses_array->set(count++, Smi::FromInt(i));
}
- return MaybeHandle<FixedArray>();
}
- double asm_wasm_time = asm_wasm_timer.Elapsed().InMillisecondsF();
- wasm::ZoneBuffer* module = asm_wasm_result.module_bytes;
- wasm::ZoneBuffer* asm_offsets = asm_wasm_result.asm_offset_table;
+ double asm_wasm_time = asm_wasm_timer.Elapsed().InMillisecondsF();
Vector<const byte> asm_offsets_vec(asm_offsets->begin(),
static_cast<int>(asm_offsets->size()));
base::ElapsedTimer compile_timer;
compile_timer.Start();
+ ErrorThrower thrower(info->isolate(), "Asm.js -> WebAssembly conversion");
MaybeHandle<JSObject> compiled = SyncCompileTranslatedAsmJs(
info->isolate(), &thrower,
wasm::ModuleWireBytes(module->begin(), module->end()), info->script(),
asm_offsets_vec);
DCHECK(!compiled.is_null());
+ DCHECK(!thrower.error());
double compile_time = compile_timer.Elapsed().InMillisecondsF();
DCHECK_GE(module->end(), module->begin());
uintptr_t wasm_size = module->end() - module->begin();
- wasm::AsmTyper::StdlibSet uses = builder.typer()->StdlibUses();
- Handle<FixedArray> uses_array =
- info->isolate()->factory()->NewFixedArray(static_cast<int>(uses.size()));
- int count = 0;
- for (auto i : uses) {
- uses_array->set(count++, Smi::FromInt(i));
- }
-
Handle<FixedArray> result =
info->isolate()->factory()->NewFixedArray(kWasmDataEntryCount);
result->set(kWasmDataCompiledModule, *compiled.ToHandleChecked());
@@ -264,8 +308,6 @@ MaybeHandle<Object> AsmJs::InstantiateAsmWasm(i::Isolate* isolate,
i::Handle<i::FixedArray> foreign_globals(
i::FixedArray::cast(wasm_data->get(kWasmDataForeignGlobals)));
- ErrorThrower thrower(isolate, "Asm.js -> WebAssembly instantiation");
-
// Create the ffi object for foreign functions {"": foreign}.
Handle<JSObject> ffi_object;
if (!foreign.is_null()) {
@@ -276,40 +318,46 @@ MaybeHandle<Object> AsmJs::InstantiateAsmWasm(i::Isolate* isolate,
foreign, NONE);
}
+ ErrorThrower thrower(isolate, "Asm.js -> WebAssembly instantiation");
i::MaybeHandle<i::Object> maybe_module_object =
i::wasm::SyncInstantiate(isolate, &thrower, module, ffi_object, memory);
if (maybe_module_object.is_null()) {
+ thrower.Reify(); // Ensure exceptions do not propagate.
return MaybeHandle<Object>();
}
+ DCHECK(!thrower.error());
i::Handle<i::Object> module_object = maybe_module_object.ToHandleChecked();
- i::Handle<i::Name> init_name(isolate->factory()->InternalizeUtf8String(
- wasm::AsmWasmBuilder::foreign_init_name));
- i::Handle<i::Object> init =
- i::Object::GetProperty(module_object, init_name).ToHandleChecked();
+ if (!FLAG_fast_validate_asm) {
+ i::Handle<i::Name> init_name(isolate->factory()->InternalizeUtf8String(
+ wasm::AsmWasmBuilder::foreign_init_name));
+ i::Handle<i::Object> init =
+ i::Object::GetProperty(module_object, init_name).ToHandleChecked();
- i::Handle<i::Object> undefined(isolate->heap()->undefined_value(), isolate);
- i::Handle<i::Object>* foreign_args_array =
- new i::Handle<i::Object>[foreign_globals->length()];
- for (int j = 0; j < foreign_globals->length(); j++) {
- if (!foreign.is_null()) {
- i::MaybeHandle<i::Name> name = i::Object::ToName(
- isolate, i::Handle<i::Object>(foreign_globals->get(j), isolate));
- if (!name.is_null()) {
- i::MaybeHandle<i::Object> val =
- i::Object::GetProperty(foreign, name.ToHandleChecked());
- if (!val.is_null()) {
- foreign_args_array[j] = val.ToHandleChecked();
- continue;
+ i::Handle<i::Object> undefined(isolate->heap()->undefined_value(), isolate);
+ i::Handle<i::Object>* foreign_args_array =
+ new i::Handle<i::Object>[foreign_globals->length()];
+ for (int j = 0; j < foreign_globals->length(); j++) {
+ if (!foreign.is_null()) {
+ i::MaybeHandle<i::Name> name = i::Object::ToName(
+ isolate, i::Handle<i::Object>(foreign_globals->get(j), isolate));
+ if (!name.is_null()) {
+ i::MaybeHandle<i::Object> val =
+ i::Object::GetProperty(foreign, name.ToHandleChecked());
+ if (!val.is_null()) {
+ foreign_args_array[j] = val.ToHandleChecked();
+ continue;
+ }
}
}
+ foreign_args_array[j] = undefined;
}
- foreign_args_array[j] = undefined;
+ i::MaybeHandle<i::Object> retval =
+ i::Execution::Call(isolate, init, undefined, foreign_globals->length(),
+ foreign_args_array);
+ delete[] foreign_args_array;
+ DCHECK(!retval.is_null());
}
- i::MaybeHandle<i::Object> retval = i::Execution::Call(
- isolate, init, undefined, foreign_globals->length(), foreign_args_array);
- delete[] foreign_args_array;
- DCHECK(!retval.is_null());
i::Handle<i::Name> single_function_name(
isolate->factory()->InternalizeUtf8String(
diff --git a/deps/v8/src/asmjs/asm-names.h b/deps/v8/src/asmjs/asm-names.h
new file mode 100644
index 0000000000..1cc151dc4c
--- /dev/null
+++ b/deps/v8/src/asmjs/asm-names.h
@@ -0,0 +1,110 @@
+// Copyright 2017 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.
+
+#ifndef V8_ASMJS_ASM_NAMES_H_
+#define V8_ASMJS_ASM_NAMES_H_
+
+#define STDLIB_MATH_VALUE_LIST(V) \
+ V(E) \
+ V(LN10) \
+ V(LN2) \
+ V(LOG2E) \
+ V(LOG10E) \
+ V(PI) \
+ V(SQRT1_2) \
+ V(SQRT2)
+
+// V(stdlib.Math.<name>, Name, wasm-opcode, asm-js-type)
+#define STDLIB_MATH_FUNCTION_MONOMORPHIC_LIST(V) \
+ V(acos, Acos, kExprF64Acos, dq2d) \
+ V(asin, Asin, kExprF64Asin, dq2d) \
+ V(atan, Atan, kExprF64Atan, dq2d) \
+ V(cos, Cos, kExprF64Cos, dq2d) \
+ V(sin, Sin, kExprF64Sin, dq2d) \
+ V(tan, Tan, kExprF64Tan, dq2d) \
+ V(exp, Exp, kExprF64Exp, dq2d) \
+ V(log, Log, kExprF64Log, dq2d) \
+ V(atan2, Atan2, kExprF64Atan2, dqdq2d) \
+ V(pow, Pow, kExprF64Pow, dqdq2d) \
+ V(imul, Imul, kExprI32Mul, ii2s) \
+ V(clz32, Clz32, kExprI32Clz, i2s)
+
+// V(stdlib.Math.<name>, Name, unused, asm-js-type)
+#define STDLIB_MATH_FUNCTION_CEIL_LIKE_LIST(V) \
+ V(ceil, Ceil, x, ceil_like) \
+ V(floor, Floor, x, ceil_like) \
+ V(sqrt, Sqrt, x, ceil_like)
+
+// V(stdlib.Math.<name>, Name, unused, asm-js-type)
+#define STDLIB_MATH_FUNCTION_LIST(V) \
+ V(min, Min, x, minmax) \
+ V(max, Max, x, minmax) \
+ V(abs, Abs, x, abs) \
+ V(fround, Fround, x, fround) \
+ STDLIB_MATH_FUNCTION_MONOMORPHIC_LIST(V) \
+ STDLIB_MATH_FUNCTION_CEIL_LIKE_LIST(V)
+
+// V(stdlib.<name>, wasm-load-type, wasm-store-type, wasm-type)
+#define STDLIB_ARRAY_TYPE_LIST(V) \
+ V(Int8Array, Mem8S, Mem8, I32) \
+ V(Uint8Array, Mem8U, Mem8, I32) \
+ V(Int16Array, Mem16S, Mem16, I32) \
+ V(Uint16Array, Mem16U, Mem16, I32) \
+ V(Int32Array, Mem, Mem, I32) \
+ V(Uint32Array, Mem, Mem, I32) \
+ V(Float32Array, Mem, Mem, F32) \
+ V(Float64Array, Mem, Mem, F64)
+
+#define STDLIB_OTHER_LIST(V) \
+ V(Infinity) \
+ V(NaN) \
+ V(Math)
+
+// clang-format off (for return)
+#define KEYWORD_NAME_LIST(V) \
+ V(arguments) \
+ V(break) \
+ V(case) \
+ V(const) \
+ V(continue) \
+ V(default) \
+ V(do) \
+ V(else) \
+ V(eval) \
+ V(for) \
+ V(function) \
+ V(if) \
+ V(new) \
+ V(return ) \
+ V(switch) \
+ V(var) \
+ V(while)
+// clang-format on
+
+// V(token-string, token-name)
+#define LONG_SYMBOL_NAME_LIST(V) \
+ V("<=", LE) \
+ V(">=", GE) \
+ V("==", EQ) \
+ V("!=", NE) \
+ V("<<", SHL) \
+ V(">>", SAR) \
+ V(">>>", SHR) \
+ V("'use asm'", UseAsm)
+
+// clang-format off
+#define SIMPLE_SINGLE_TOKEN_LIST(V) \
+ V('+') V('-') V('*') V('%') V('~') V('^') V('&') V('|') V('(') V(')') \
+ V('[') V(']') V('{') V('}') V(':') V(';') V(',') V('?')
+// clang-format on
+
+// V(name, value, string-name)
+#define SPECIAL_TOKEN_LIST(V) \
+ V(kUninitialized, 0, "{uninitalized}") \
+ V(kEndOfInput, -1, "{end of input}") \
+ V(kParseError, -2, "{parse error}") \
+ V(kUnsigned, -3, "{unsigned value}") \
+ V(kDouble, -4, "{double value}")
+
+#endif
diff --git a/deps/v8/src/asmjs/asm-parser.cc b/deps/v8/src/asmjs/asm-parser.cc
new file mode 100644
index 0000000000..c18f7d1bf2
--- /dev/null
+++ b/deps/v8/src/asmjs/asm-parser.cc
@@ -0,0 +1,2449 @@
+// Copyright 2017 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.
+
+#include "src/asmjs/asm-parser.h"
+
+// Required to get M_E etc. for MSVC.
+// References from STDLIB_MATH_VALUE_LIST in asm-names.h
+#if defined(_WIN32)
+#define _USE_MATH_DEFINES
+#endif
+#include <math.h>
+#include <string.h>
+
+#include <algorithm>
+
+#include "src/asmjs/asm-types.h"
+#include "src/objects-inl.h"
+#include "src/objects.h"
+#include "src/parsing/scanner-character-streams.h"
+#include "src/wasm/wasm-macro-gen.h"
+#include "src/wasm/wasm-opcodes.h"
+
+namespace v8 {
+namespace internal {
+namespace wasm {
+
+#ifdef DEBUG
+#define FAIL_AND_RETURN(ret, msg) \
+ failed_ = true; \
+ failure_message_ = msg; \
+ failure_location_ = scanner_.GetPosition(); \
+ if (FLAG_trace_asm_parser) { \
+ PrintF("[asm.js failure: %s, token: '%s', see: %s:%d]\n", msg, \
+ scanner_.Name(scanner_.Token()).c_str(), __FILE__, __LINE__); \
+ } \
+ return ret;
+#else
+#define FAIL_AND_RETURN(ret, msg) \
+ failed_ = true; \
+ failure_message_ = msg; \
+ failure_location_ = scanner_.GetPosition(); \
+ return ret;
+#endif
+
+#define FAIL(msg) FAIL_AND_RETURN(, msg)
+#define FAILn(msg) FAIL_AND_RETURN(nullptr, msg)
+#define FAILf(msg) FAIL_AND_RETURN(false, msg)
+
+#define EXPECT_TOKEN_OR_RETURN(ret, token) \
+ do { \
+ if (scanner_.Token() != token) { \
+ FAIL_AND_RETURN(ret, "Unexpected token"); \
+ } \
+ scanner_.Next(); \
+ } while (false)
+
+#define EXPECT_TOKEN(token) EXPECT_TOKEN_OR_RETURN(, token)
+#define EXPECT_TOKENn(token) EXPECT_TOKEN_OR_RETURN(nullptr, token)
+#define EXPECT_TOKENf(token) EXPECT_TOKEN_OR_RETURN(false, token)
+
+#define RECURSE_OR_RETURN(ret, call) \
+ do { \
+ DCHECK(!failed_); \
+ if (GetCurrentStackPosition() < stack_limit_) { \
+ FAIL_AND_RETURN(ret, "Stack overflow while parsing asm.js module."); \
+ } \
+ call; \
+ if (failed_) return ret; \
+ } while (false)
+
+#define RECURSE(call) RECURSE_OR_RETURN(, call)
+#define RECURSEn(call) RECURSE_OR_RETURN(nullptr, call)
+#define RECURSEf(call) RECURSE_OR_RETURN(false, call)
+
+#define TOK(name) AsmJsScanner::kToken_##name
+
+AsmJsParser::AsmJsParser(Isolate* isolate, Zone* zone, Handle<Script> script,
+ int start, int end)
+ : zone_(zone),
+ module_builder_(new (zone) WasmModuleBuilder(zone)),
+ return_type_(nullptr),
+ stack_limit_(isolate->stack_guard()->real_climit()),
+ global_var_info_(zone),
+ local_var_info_(zone),
+ failed_(false),
+ failure_location_(start),
+ stdlib_name_(kTokenNone),
+ foreign_name_(kTokenNone),
+ heap_name_(kTokenNone),
+ inside_heap_assignment_(false),
+ heap_access_type_(nullptr),
+ block_stack_(zone),
+ pending_label_(0),
+ global_imports_(zone) {
+ InitializeStdlibTypes();
+ Handle<String> source(String::cast(script->source()), isolate);
+ std::unique_ptr<Utf16CharacterStream> stream(
+ ScannerStream::For(source, start, end));
+ scanner_.SetStream(std::move(stream));
+}
+
+void AsmJsParser::InitializeStdlibTypes() {
+ auto* d = AsmType::Double();
+ auto* dq = AsmType::DoubleQ();
+ stdlib_dq2d_ = AsmType::Function(zone(), d);
+ stdlib_dq2d_->AsFunctionType()->AddArgument(dq);
+
+ stdlib_dqdq2d_ = AsmType::Function(zone(), d);
+ stdlib_dqdq2d_->AsFunctionType()->AddArgument(dq);
+ stdlib_dqdq2d_->AsFunctionType()->AddArgument(dq);
+
+ auto* f = AsmType::Float();
+ auto* fq = AsmType::FloatQ();
+ stdlib_fq2f_ = AsmType::Function(zone(), f);
+ stdlib_fq2f_->AsFunctionType()->AddArgument(fq);
+
+ auto* s = AsmType::Signed();
+ auto* s2s = AsmType::Function(zone(), s);
+ s2s->AsFunctionType()->AddArgument(s);
+
+ auto* i = AsmType::Int();
+ stdlib_i2s_ = AsmType::Function(zone_, s);
+ stdlib_i2s_->AsFunctionType()->AddArgument(i);
+
+ stdlib_ii2s_ = AsmType::Function(zone(), s);
+ stdlib_ii2s_->AsFunctionType()->AddArgument(i);
+ stdlib_ii2s_->AsFunctionType()->AddArgument(i);
+
+ auto* minmax_d = AsmType::MinMaxType(zone(), d, d);
+ // *VIOLATION* The float variant is not part of the spec, but firefox accepts
+ // it.
+ auto* minmax_f = AsmType::MinMaxType(zone(), f, f);
+ auto* minmax_i = AsmType::MinMaxType(zone(), s, i);
+ stdlib_minmax_ = AsmType::OverloadedFunction(zone());
+ stdlib_minmax_->AsOverloadedFunctionType()->AddOverload(minmax_i);
+ stdlib_minmax_->AsOverloadedFunctionType()->AddOverload(minmax_f);
+ stdlib_minmax_->AsOverloadedFunctionType()->AddOverload(minmax_d);
+
+ stdlib_abs_ = AsmType::OverloadedFunction(zone());
+ stdlib_abs_->AsOverloadedFunctionType()->AddOverload(s2s);
+ stdlib_abs_->AsOverloadedFunctionType()->AddOverload(stdlib_dq2d_);
+ stdlib_abs_->AsOverloadedFunctionType()->AddOverload(stdlib_fq2f_);
+
+ stdlib_ceil_like_ = AsmType::OverloadedFunction(zone());
+ stdlib_ceil_like_->AsOverloadedFunctionType()->AddOverload(stdlib_dq2d_);
+ stdlib_ceil_like_->AsOverloadedFunctionType()->AddOverload(stdlib_fq2f_);
+
+ stdlib_fround_ = AsmType::FroundType(zone());
+}
+
+FunctionSig* AsmJsParser::ConvertSignature(
+ AsmType* return_type, const std::vector<AsmType*>& params) {
+ FunctionSig::Builder sig_builder(
+ zone(), !return_type->IsA(AsmType::Void()) ? 1 : 0, params.size());
+ for (auto param : params) {
+ if (param->IsA(AsmType::Double())) {
+ sig_builder.AddParam(kWasmF64);
+ } else if (param->IsA(AsmType::Float())) {
+ sig_builder.AddParam(kWasmF32);
+ } else if (param->IsA(AsmType::Int())) {
+ sig_builder.AddParam(kWasmI32);
+ } else {
+ return nullptr;
+ }
+ }
+ if (!return_type->IsA(AsmType::Void())) {
+ if (return_type->IsA(AsmType::Double())) {
+ sig_builder.AddReturn(kWasmF64);
+ } else if (return_type->IsA(AsmType::Float())) {
+ sig_builder.AddReturn(kWasmF32);
+ } else if (return_type->IsA(AsmType::Signed())) {
+ sig_builder.AddReturn(kWasmI32);
+ } else {
+ return 0;
+ }
+ }
+ return sig_builder.Build();
+}
+
+bool AsmJsParser::Run() {
+ ValidateModule();
+ return !failed_;
+}
+
+class AsmJsParser::TemporaryVariableScope {
+ public:
+ explicit TemporaryVariableScope(AsmJsParser* parser) : parser_(parser) {
+ local_depth_ = parser_->function_temp_locals_depth_;
+ parser_->function_temp_locals_depth_++;
+ }
+ ~TemporaryVariableScope() {
+ DCHECK_EQ(local_depth_, parser_->function_temp_locals_depth_ - 1);
+ parser_->function_temp_locals_depth_--;
+ }
+ uint32_t get() const { return parser_->TempVariable(local_depth_); }
+
+ private:
+ AsmJsParser* parser_;
+ int local_depth_;
+};
+
+AsmJsParser::VarInfo::VarInfo()
+ : type(AsmType::None()),
+ function_builder(nullptr),
+ import(nullptr),
+ mask(-1),
+ index(0),
+ kind(VarKind::kUnused),
+ mutable_variable(true),
+ function_defined(false) {}
+
+wasm::AsmJsParser::VarInfo* AsmJsParser::GetVarInfo(
+ AsmJsScanner::token_t token) {
+ if (AsmJsScanner::IsGlobal(token)) {
+ size_t old = global_var_info_.size();
+ size_t index = AsmJsScanner::GlobalIndex(token);
+ size_t sz = std::max(old, index + 1);
+ if (sz != old) {
+ global_var_info_.resize(sz);
+ }
+ return &global_var_info_[index];
+ } else if (AsmJsScanner::IsLocal(token)) {
+ size_t old = local_var_info_.size();
+ size_t index = AsmJsScanner::LocalIndex(token);
+ size_t sz = std::max(old, index + 1);
+ if (sz != old) {
+ local_var_info_.resize(sz);
+ }
+ return &local_var_info_[index];
+ }
+ UNREACHABLE();
+ return nullptr;
+}
+
+uint32_t AsmJsParser::VarIndex(VarInfo* info) {
+ if (info->import != nullptr) {
+ return info->index;
+ } else {
+ return info->index + static_cast<uint32_t>(global_imports_.size());
+ }
+}
+
+void AsmJsParser::AddGlobalImport(std::string name, AsmType* type,
+ ValueType vtype, bool mutable_variable,
+ VarInfo* info) {
+ // TODO(bradnelson): Refactor memory management here.
+ // AsmModuleBuilder should really own import names.
+ char* name_data = zone()->NewArray<char>(name.size());
+ memcpy(name_data, name.data(), name.size());
+ if (mutable_variable) {
+ // Allocate a separate variable for the import.
+ DeclareGlobal(info, true, type, vtype);
+ // Record the need to initialize the global from the import.
+ global_imports_.push_back({name_data, name.size(), 0, info->index, true});
+ } else {
+ // Just use the import directly.
+ global_imports_.push_back({name_data, name.size(), 0, info->index, false});
+ }
+ GlobalImport& gi = global_imports_.back();
+ // TODO(bradnelson): Reuse parse buffer memory / make wasm-module-builder
+ // managed the memory for the import name (currently have to keep our
+ // own memory for it).
+ gi.import_index = module_builder_->AddGlobalImport(
+ name_data, static_cast<int>(name.size()), vtype);
+ if (!mutable_variable) {
+ info->DeclareGlobalImport(type, gi.import_index);
+ }
+}
+
+void AsmJsParser::VarInfo::DeclareGlobalImport(AsmType* type, uint32_t index) {
+ kind = VarKind::kGlobal;
+ this->type = type;
+ this->index = index;
+ mutable_variable = false;
+}
+
+void AsmJsParser::VarInfo::DeclareStdlibFunc(VarKind kind, AsmType* type) {
+ this->kind = kind;
+ this->type = type;
+ index = 0; // unused
+ mutable_variable = false;
+}
+
+void AsmJsParser::DeclareGlobal(VarInfo* info, bool mutable_variable,
+ AsmType* type, ValueType vtype,
+ const WasmInitExpr& init) {
+ info->kind = VarKind::kGlobal;
+ info->type = type;
+ info->index = module_builder_->AddGlobal(vtype, false, true, init);
+ info->mutable_variable = mutable_variable;
+}
+
+uint32_t AsmJsParser::TempVariable(int index) {
+ if (index + 1 > function_temp_locals_used_) {
+ function_temp_locals_used_ = index + 1;
+ }
+ return function_temp_locals_offset_ + index;
+}
+
+void AsmJsParser::SkipSemicolon() {
+ if (Check(';')) {
+ // Had a semicolon.
+ } else if (!Peek('}') && !scanner_.IsPrecededByNewline()) {
+ FAIL("Expected ;");
+ }
+}
+
+void AsmJsParser::Begin(AsmJsScanner::token_t label) {
+ BareBegin(BlockKind::kRegular, label);
+ current_function_builder_->EmitWithU8(kExprBlock, kLocalVoid);
+}
+
+void AsmJsParser::Loop(AsmJsScanner::token_t label) {
+ BareBegin(BlockKind::kLoop, label);
+ current_function_builder_->EmitWithU8(kExprLoop, kLocalVoid);
+}
+
+void AsmJsParser::End() {
+ BareEnd();
+ current_function_builder_->Emit(kExprEnd);
+}
+
+void AsmJsParser::BareBegin(BlockKind kind, AsmJsScanner::token_t label) {
+ BlockInfo info;
+ info.kind = kind;
+ info.label = label;
+ block_stack_.push_back(info);
+}
+
+void AsmJsParser::BareEnd() {
+ DCHECK(block_stack_.size() > 0);
+ block_stack_.pop_back();
+}
+
+int AsmJsParser::FindContinueLabelDepth(AsmJsScanner::token_t label) {
+ int count = 0;
+ for (auto it = block_stack_.rbegin(); it != block_stack_.rend();
+ ++it, ++count) {
+ if (it->kind == BlockKind::kLoop &&
+ (label == kTokenNone || it->label == label)) {
+ return count;
+ }
+ }
+ return -1;
+}
+
+int AsmJsParser::FindBreakLabelDepth(AsmJsScanner::token_t label) {
+ int count = 0;
+ for (auto it = block_stack_.rbegin(); it != block_stack_.rend();
+ ++it, ++count) {
+ if (it->kind == BlockKind::kRegular &&
+ (label == kTokenNone || it->label == label)) {
+ return count;
+ }
+ }
+ return -1;
+}
+
+// 6.1 ValidateModule
+void AsmJsParser::ValidateModule() {
+ RECURSE(ValidateModuleParameters());
+ EXPECT_TOKEN('{');
+ EXPECT_TOKEN(TOK(UseAsm));
+ SkipSemicolon();
+ RECURSE(ValidateModuleVars());
+ while (Peek(TOK(function))) {
+ RECURSE(ValidateFunction());
+ }
+ while (Peek(TOK(var))) {
+ RECURSE(ValidateFunctionTable());
+ }
+ RECURSE(ValidateExport());
+
+ // Check that all functions were eventually defined.
+ for (auto& info : global_var_info_) {
+ if (info.kind == VarKind::kFunction && !info.function_defined) {
+ FAIL("Undefined function");
+ }
+ if (info.kind == VarKind::kTable && !info.function_defined) {
+ FAIL("Undefined function table");
+ }
+ }
+
+ // Add start function to init things.
+ WasmFunctionBuilder* start = module_builder_->AddFunction();
+ module_builder_->MarkStartFunction(start);
+ for (auto global_import : global_imports_) {
+ if (global_import.needs_init) {
+ start->EmitWithVarInt(kExprGetGlobal, global_import.import_index);
+ start->EmitWithVarInt(kExprSetGlobal,
+ static_cast<uint32_t>(global_import.global_index +
+ global_imports_.size()));
+ }
+ }
+ start->Emit(kExprEnd);
+ FunctionSig::Builder b(zone(), 0, 0);
+ start->SetSignature(b.Build());
+}
+
+// 6.1 ValidateModule - parameters
+void AsmJsParser::ValidateModuleParameters() {
+ EXPECT_TOKEN('(');
+ stdlib_name_ = 0;
+ foreign_name_ = 0;
+ heap_name_ = 0;
+ if (!Peek(')')) {
+ if (!scanner_.IsGlobal()) {
+ FAIL("Expected stdlib parameter");
+ }
+ stdlib_name_ = Consume();
+ if (!Peek(')')) {
+ EXPECT_TOKEN(',');
+ if (!scanner_.IsGlobal()) {
+ FAIL("Expected foreign parameter");
+ }
+ foreign_name_ = Consume();
+ if (!Peek(')')) {
+ EXPECT_TOKEN(',');
+ if (!scanner_.IsGlobal()) {
+ FAIL("Expected heap parameter");
+ }
+ heap_name_ = Consume();
+ }
+ }
+ }
+ EXPECT_TOKEN(')');
+}
+
+// 6.1 ValidateModule - variables
+void AsmJsParser::ValidateModuleVars() {
+ while (Peek(TOK(var)) || Peek(TOK(const))) {
+ bool mutable_variable = true;
+ if (Check(TOK(var))) {
+ // Had a var.
+ } else {
+ EXPECT_TOKEN(TOK(const));
+ mutable_variable = false;
+ }
+ for (;;) {
+ RECURSE(ValidateModuleVar(mutable_variable));
+ if (Check(',')) {
+ continue;
+ }
+ break;
+ }
+ SkipSemicolon();
+ }
+}
+
+// 6.1 ValidateModule - one variable
+void AsmJsParser::ValidateModuleVar(bool mutable_variable) {
+ if (!scanner_.IsGlobal()) {
+ FAIL("Expected identifier");
+ }
+ VarInfo* info = GetVarInfo(Consume());
+ if (info->kind != VarKind::kUnused) {
+ FAIL("Redefinition of variable");
+ }
+ EXPECT_TOKEN('=');
+ double dvalue = 0.0;
+ uint64_t uvalue = 0;
+ if (CheckForDouble(&dvalue)) {
+ DeclareGlobal(info, mutable_variable, AsmType::Double(), kWasmF64,
+ WasmInitExpr(dvalue));
+ } else if (CheckForUnsigned(&uvalue)) {
+ if (uvalue > 0x7fffffff) {
+ FAIL("Numeric literal out of range");
+ }
+ DeclareGlobal(info, mutable_variable,
+ mutable_variable ? AsmType::Int() : AsmType::Signed(),
+ kWasmI32, WasmInitExpr(static_cast<int32_t>(uvalue)));
+ } else if (Check('-')) {
+ if (CheckForDouble(&dvalue)) {
+ DeclareGlobal(info, mutable_variable, AsmType::Double(), kWasmF64,
+ WasmInitExpr(-dvalue));
+ } else if (CheckForUnsigned(&uvalue)) {
+ if (uvalue > 0x7fffffff) {
+ FAIL("Numeric literal out of range");
+ }
+ DeclareGlobal(info, mutable_variable,
+ mutable_variable ? AsmType::Int() : AsmType::Signed(),
+ kWasmI32, WasmInitExpr(-static_cast<int32_t>(uvalue)));
+ } else {
+ FAIL("Expected numeric literal");
+ }
+ } else if (Check(TOK(new))) {
+ RECURSE(ValidateModuleVarNewStdlib(info));
+ } else if (Check(stdlib_name_)) {
+ EXPECT_TOKEN('.');
+ RECURSE(ValidateModuleVarStdlib(info));
+ } else if (ValidateModuleVarImport(info, mutable_variable)) {
+ // Handled inside.
+ } else if (scanner_.IsGlobal()) {
+ RECURSE(ValidateModuleVarFromGlobal(info, mutable_variable));
+ } else {
+ FAIL("Bad variable declaration");
+ }
+}
+
+// 6.1 ValidateModule - global float declaration
+void AsmJsParser::ValidateModuleVarFromGlobal(VarInfo* info,
+ bool mutable_variable) {
+ VarInfo* src_info = GetVarInfo(Consume());
+ if (!src_info->type->IsA(stdlib_fround_)) {
+ if (src_info->mutable_variable) {
+ FAIL("Can only use immutable variables in global definition");
+ }
+ if (mutable_variable) {
+ FAIL("Can only define immutable variables with other immutables");
+ }
+ if (!src_info->type->IsA(AsmType::Int()) &&
+ !src_info->type->IsA(AsmType::Float()) &&
+ !src_info->type->IsA(AsmType::Double())) {
+ FAIL("Expected int, float, double, or fround for global definition");
+ }
+ info->kind = VarKind::kGlobal;
+ info->type = src_info->type;
+ info->index = src_info->index;
+ info->mutable_variable = false;
+ return;
+ }
+ EXPECT_TOKEN('(');
+ bool negate = false;
+ if (Check('-')) {
+ negate = true;
+ }
+ double dvalue = 0.0;
+ uint64_t uvalue = 0;
+ if (CheckForDouble(&dvalue)) {
+ if (negate) {
+ dvalue = -dvalue;
+ }
+ DeclareGlobal(info, mutable_variable, AsmType::Float(), kWasmF32,
+ WasmInitExpr(static_cast<float>(dvalue)));
+ } else if (CheckForUnsigned(&uvalue)) {
+ dvalue = uvalue;
+ if (negate) {
+ dvalue = -dvalue;
+ }
+ DeclareGlobal(info, mutable_variable, AsmType::Float(), kWasmF32,
+ WasmInitExpr(static_cast<float>(dvalue)));
+ } else {
+ FAIL("Expected numeric literal");
+ }
+ EXPECT_TOKEN(')');
+}
+
+// 6.1 ValidateModule - foreign imports
+bool AsmJsParser::ValidateModuleVarImport(VarInfo* info,
+ bool mutable_variable) {
+ if (Check('+')) {
+ EXPECT_TOKENf(foreign_name_);
+ EXPECT_TOKENf('.');
+ AddGlobalImport(scanner_.GetIdentifierString(), AsmType::Double(), kWasmF64,
+ mutable_variable, info);
+ scanner_.Next();
+ return true;
+ } else if (Check(foreign_name_)) {
+ EXPECT_TOKENf('.');
+ std::string import_name = scanner_.GetIdentifierString();
+ scanner_.Next();
+ if (Check('|')) {
+ if (!CheckForZero()) {
+ FAILf("Expected |0 type annotation for foreign integer import");
+ }
+ AddGlobalImport(import_name, AsmType::Int(), kWasmI32, mutable_variable,
+ info);
+ return true;
+ }
+ info->kind = VarKind::kImportedFunction;
+ function_import_info_.resize(function_import_info_.size() + 1);
+ info->import = &function_import_info_.back();
+ // TODO(bradnelson): Refactor memory management here.
+ // AsmModuleBuilder should really own import names.
+ info->import->function_name = zone()->NewArray<char>(import_name.size());
+ memcpy(info->import->function_name, import_name.data(), import_name.size());
+ info->import->function_name_size = import_name.size();
+ return true;
+ }
+ return false;
+}
+
+// 6.1 ValidateModule - one variable
+// 9 - Standard Library - heap types
+void AsmJsParser::ValidateModuleVarNewStdlib(VarInfo* info) {
+ EXPECT_TOKEN(stdlib_name_);
+ EXPECT_TOKEN('.');
+ switch (Consume()) {
+#define V(name, _junk1, _junk2, _junk3) \
+ case TOK(name): \
+ info->DeclareStdlibFunc(VarKind::kSpecial, AsmType::name()); \
+ break;
+ STDLIB_ARRAY_TYPE_LIST(V)
+#undef V
+ default:
+ FAIL("Expected ArrayBuffer view");
+ break;
+ }
+ EXPECT_TOKEN('(');
+ EXPECT_TOKEN(heap_name_);
+ EXPECT_TOKEN(')');
+}
+
+// 6.1 ValidateModule - one variable
+// 9 - Standard Library
+void AsmJsParser::ValidateModuleVarStdlib(VarInfo* info) {
+ if (Check(TOK(Math))) {
+ EXPECT_TOKEN('.');
+ switch (Consume()) {
+#define V(name) \
+ case TOK(name): \
+ DeclareGlobal(info, false, AsmType::Double(), kWasmF64, \
+ WasmInitExpr(M_##name)); \
+ stdlib_uses_.insert(AsmTyper::kMath##name); \
+ break;
+ STDLIB_MATH_VALUE_LIST(V)
+#undef V
+#define V(name, Name, op, sig) \
+ case TOK(name): \
+ info->DeclareStdlibFunc(VarKind::kMath##Name, stdlib_##sig##_); \
+ stdlib_uses_.insert(AsmTyper::kMath##Name); \
+ break;
+ STDLIB_MATH_FUNCTION_LIST(V)
+#undef V
+ default:
+ FAIL("Invalid member of stdlib.Math");
+ }
+ } else if (Check(TOK(Infinity))) {
+ DeclareGlobal(info, false, AsmType::Double(), kWasmF64,
+ WasmInitExpr(std::numeric_limits<double>::infinity()));
+ stdlib_uses_.insert(AsmTyper::kInfinity);
+ } else if (Check(TOK(NaN))) {
+ DeclareGlobal(info, false, AsmType::Double(), kWasmF64,
+ WasmInitExpr(std::numeric_limits<double>::quiet_NaN()));
+ stdlib_uses_.insert(AsmTyper::kNaN);
+ } else {
+ FAIL("Invalid member of stdlib");
+ }
+}
+
+// 6.2 ValidateExport
+void AsmJsParser::ValidateExport() {
+ // clang-format off
+ EXPECT_TOKEN(TOK(return));
+ // clang format on
+ if (Check('{')) {
+ for (;;) {
+ std::string name = scanner_.GetIdentifierString();
+ if (!scanner_.IsGlobal() && !scanner_.IsLocal()) {
+ FAIL("Illegal export name");
+ }
+ Consume();
+ EXPECT_TOKEN(':');
+ if (!scanner_.IsGlobal()) {
+ FAIL("Expected function name");
+ }
+ VarInfo* info = GetVarInfo(Consume());
+ if (info->kind != VarKind::kFunction) {
+ FAIL("Expected function");
+ }
+ info->function_builder->ExportAs(
+ {name.c_str(), static_cast<int>(name.size())});
+ if (Check(',')) {
+ if (!Peek('}')) {
+ continue;
+ }
+ }
+ break;
+ }
+ EXPECT_TOKEN('}');
+ } else {
+ if (!scanner_.IsGlobal()) {
+ FAIL("Single function export must be a function name");
+ }
+ VarInfo* info = GetVarInfo(Consume());
+ if (info->kind != VarKind::kFunction) {
+ FAIL("Single function export must be a function");
+ }
+ const char* single_function_name = "__single_function__";
+ info->function_builder->ExportAs(CStrVector(single_function_name));
+ }
+}
+
+// 6.3 ValidateFunctionTable
+void AsmJsParser::ValidateFunctionTable() {
+ EXPECT_TOKEN(TOK(var));
+ if (!scanner_.IsGlobal()) {
+ FAIL("Expected table name");
+ }
+ VarInfo* table_info = GetVarInfo(Consume());
+ if (table_info->kind == VarKind::kTable) {
+ if (table_info->function_defined) {
+ FAIL("Function table redefined");
+ }
+ table_info->function_defined = true;
+ } else if (table_info->kind != VarKind::kUnused) {
+ FAIL("Function table name collides");
+ }
+ EXPECT_TOKEN('=');
+ EXPECT_TOKEN('[');
+ uint64_t count = 0;
+ for (;;) {
+ if (!scanner_.IsGlobal()) {
+ FAIL("Expected function name");
+ }
+ VarInfo* info = GetVarInfo(Consume());
+ if (info->kind != VarKind::kFunction) {
+ FAIL("Expected function");
+ }
+ // Only store the function into a table if we used the table somewhere
+ // (i.e. tables are first seen at their use sites and allocated there).
+ if (table_info->kind == VarKind::kTable) {
+ DCHECK_GE(table_info->mask, 0);
+ if (count >= static_cast<uint64_t>(table_info->mask) + 1) {
+ FAIL("Exceeded function table size");
+ }
+ if (!info->type->IsA(table_info->type)) {
+ FAIL("Function table definition doesn't match use");
+ }
+ module_builder_->SetIndirectFunction(
+ static_cast<uint32_t>(table_info->index + count), info->index);
+ }
+ ++count;
+ if (Check(',')) {
+ if (!Peek(']')) {
+ continue;
+ }
+ }
+ break;
+ }
+ EXPECT_TOKEN(']');
+ if (table_info->kind == VarKind::kTable &&
+ count != static_cast<uint64_t>(table_info->mask) + 1) {
+ FAIL("Function table size does not match uses");
+ }
+ SkipSemicolon();
+}
+
+// 6.4 ValidateFunction
+void AsmJsParser::ValidateFunction() {
+ EXPECT_TOKEN(TOK(function));
+ if (!scanner_.IsGlobal()) {
+ FAIL("Expected function name");
+ }
+
+ std::string function_name_raw = scanner_.GetIdentifierString();
+ AsmJsScanner::token_t function_name = Consume();
+ VarInfo* function_info = GetVarInfo(function_name);
+ if (function_info->kind == VarKind::kUnused) {
+ function_info->kind = VarKind::kFunction;
+ function_info->function_builder = module_builder_->AddFunction();
+ function_info->index = function_info->function_builder->func_index();
+ } else if (function_info->kind != VarKind::kFunction) {
+ FAIL("Function name collides with variable");
+ } else if (function_info->function_defined) {
+ FAIL("Function redefined");
+ }
+
+ function_info->function_defined = true;
+ // TODO(bradnelson): Cleanup memory management here.
+ // WasmModuleBuilder should own these.
+ char* function_name_chr = zone()->NewArray<char>(function_name_raw.size());
+ memcpy(function_name_chr, function_name_raw.data(), function_name_raw.size());
+ function_info->function_builder->SetName(
+ {function_name_chr, static_cast<int>(function_name_raw.size())});
+ current_function_builder_ = function_info->function_builder;
+ return_type_ = nullptr;
+
+ // Record start of the function, used as position for the stack check.
+ int start_position = static_cast<int>(scanner_.Position());
+ current_function_builder_->SetAsmFunctionStartPosition(start_position);
+
+ std::vector<AsmType*> params;
+ ValidateFunctionParams(&params);
+ std::vector<ValueType> locals;
+ ValidateFunctionLocals(params.size(), &locals);
+
+ function_temp_locals_offset_ = static_cast<uint32_t>(
+ params.size() + locals.size());
+ function_temp_locals_used_ = 0;
+ function_temp_locals_depth_ = 0;
+
+ while (!failed_ && !Peek('}')) {
+ RECURSE(ValidateStatement());
+ }
+ EXPECT_TOKEN('}');
+
+ if (return_type_ == nullptr) {
+ return_type_ = AsmType::Void();
+ }
+
+ // TODO(bradnelson): WasmModuleBuilder can't take this in the right order.
+ // We should fix that so we can use it instead.
+ FunctionSig* sig = ConvertSignature(return_type_, params);
+ if (sig == nullptr) {
+ FAIL("Invalid function signature in declaration");
+ }
+ current_function_builder_->SetSignature(sig);
+ for (auto local : locals) {
+ current_function_builder_->AddLocal(local);
+ }
+ // Add bonus temps.
+ for (int i = 0; i < function_temp_locals_used_; ++i) {
+ current_function_builder_->AddLocal(kWasmI32);
+ }
+
+ // End function
+ current_function_builder_->Emit(kExprEnd);
+
+ // Record (or validate) function type.
+ AsmType* function_type = AsmType::Function(zone(), return_type_);
+ for (auto t : params) {
+ function_type->AsFunctionType()->AddArgument(t);
+ }
+ function_info = GetVarInfo(function_name);
+ if (function_info->type->IsA(AsmType::None())) {
+ DCHECK(function_info->kind == VarKind::kFunction);
+ function_info->type = function_type;
+ } else if (!function_type->IsA(function_info->type)) {
+ // TODO(bradnelson): Should IsExactly be used here?
+ FAIL("Function definition doesn't match use");
+ }
+
+ scanner_.ResetLocals();
+ local_var_info_.clear();
+}
+
+// 6.4 ValidateFunction
+void AsmJsParser::ValidateFunctionParams(std::vector<AsmType*>* params) {
+ // TODO(bradnelson): Do this differently so that the scanner doesn't need to
+ // have a state transition that needs knowledge of how the scanner works
+ // inside.
+ scanner_.EnterLocalScope();
+ EXPECT_TOKEN('(');
+ std::vector<AsmJsScanner::token_t> function_parameters;
+ while (!failed_ && !Peek(')')) {
+ if (!scanner_.IsLocal()) {
+ FAIL("Expected parameter name");
+ }
+ function_parameters.push_back(Consume());
+ if (!Peek(')')) {
+ EXPECT_TOKEN(',');
+ }
+ }
+ EXPECT_TOKEN(')');
+ scanner_.EnterGlobalScope();
+ EXPECT_TOKEN('{');
+ // 5.1 Parameter Type Annotations
+ for (auto p : function_parameters) {
+ EXPECT_TOKEN(p);
+ EXPECT_TOKEN('=');
+ VarInfo* info = GetVarInfo(p);
+ if (info->kind != VarKind::kUnused) {
+ FAIL("Duplicate parameter name");
+ }
+ if (Check(p)) {
+ EXPECT_TOKEN('|');
+ if (!CheckForZero()) {
+ FAIL("Bad integer parameter annotation.");
+ }
+ info->kind = VarKind::kLocal;
+ info->type = AsmType::Int();
+ info->index = static_cast<uint32_t>(params->size());
+ params->push_back(AsmType::Int());
+ } else if (Check('+')) {
+ EXPECT_TOKEN(p);
+ info->kind = VarKind::kLocal;
+ info->type = AsmType::Double();
+ info->index = static_cast<uint32_t>(params->size());
+ params->push_back(AsmType::Double());
+ } else {
+ if (!GetVarInfo(Consume())->type->IsA(stdlib_fround_)) {
+ FAIL("Expected fround");
+ }
+ EXPECT_TOKEN('(');
+ EXPECT_TOKEN(p);
+ EXPECT_TOKEN(')');
+ info->kind = VarKind::kLocal;
+ info->type = AsmType::Float();
+ info->index = static_cast<uint32_t>(params->size());
+ params->push_back(AsmType::Float());
+ }
+ SkipSemicolon();
+ }
+}
+
+// 6.4 ValidateFunction - locals
+void AsmJsParser::ValidateFunctionLocals(
+ size_t param_count, std::vector<ValueType>* locals) {
+ // Local Variables.
+ while (Peek(TOK(var))) {
+ scanner_.EnterLocalScope();
+ EXPECT_TOKEN(TOK(var));
+ scanner_.EnterGlobalScope();
+ for (;;) {
+ if (!scanner_.IsLocal()) {
+ FAIL("Expected local variable identifier");
+ }
+ VarInfo* info = GetVarInfo(Consume());
+ if (info->kind != VarKind::kUnused) {
+ FAIL("Duplicate local variable name");
+ }
+ // Store types.
+ EXPECT_TOKEN('=');
+ double dvalue = 0.0;
+ uint64_t uvalue = 0;
+ if (Check('-')) {
+ if (CheckForDouble(&dvalue)) {
+ info->kind = VarKind::kLocal;
+ info->type = AsmType::Double();
+ info->index = static_cast<uint32_t>(param_count + locals->size());
+ locals->push_back(kWasmF64);
+ byte code[] = {WASM_F64(-dvalue)};
+ current_function_builder_->EmitCode(code, sizeof(code));
+ current_function_builder_->EmitSetLocal(info->index);
+ } else if (CheckForUnsigned(&uvalue)) {
+ if (uvalue > 0x7fffffff) {
+ FAIL("Numeric literal out of range");
+ }
+ info->kind = VarKind::kLocal;
+ info->type = AsmType::Int();
+ info->index = static_cast<uint32_t>(param_count + locals->size());
+ locals->push_back(kWasmI32);
+ int32_t value = -static_cast<int32_t>(uvalue);
+ current_function_builder_->EmitI32Const(value);
+ current_function_builder_->EmitSetLocal(info->index);
+ } else {
+ FAIL("Expected variable initial value");
+ }
+ } else if (scanner_.IsGlobal()) {
+ VarInfo* sinfo = GetVarInfo(Consume());
+ if (sinfo->kind == VarKind::kGlobal) {
+ if (sinfo->mutable_variable) {
+ FAIL("Initializing from global requires const variable");
+ }
+ info->kind = VarKind::kLocal;
+ info->type = sinfo->type;
+ info->index = static_cast<uint32_t>(param_count + locals->size());
+ if (sinfo->type->IsA(AsmType::Int())) {
+ locals->push_back(kWasmI32);
+ } else if (sinfo->type->IsA(AsmType::Float())) {
+ locals->push_back(kWasmF32);
+ } else if (sinfo->type->IsA(AsmType::Double())) {
+ locals->push_back(kWasmF64);
+ } else {
+ FAIL("Bad local variable definition");
+ }
+ current_function_builder_->EmitWithVarInt(kExprGetGlobal,
+ VarIndex(sinfo));
+ current_function_builder_->EmitSetLocal(info->index);
+ } else if (sinfo->type->IsA(stdlib_fround_)) {
+ EXPECT_TOKEN('(');
+ bool negate = false;
+ if (Check('-')) {
+ negate = true;
+ }
+ double dvalue = 0.0;
+ if (CheckForDouble(&dvalue)) {
+ info->kind = VarKind::kLocal;
+ info->type = AsmType::Float();
+ info->index = static_cast<uint32_t>(param_count + locals->size());
+ locals->push_back(kWasmF32);
+ if (negate) {
+ dvalue = -dvalue;
+ }
+ byte code[] = {WASM_F32(dvalue)};
+ current_function_builder_->EmitCode(code, sizeof(code));
+ current_function_builder_->EmitSetLocal(info->index);
+ } else if (CheckForUnsigned(&uvalue)) {
+ if (uvalue > 0x7fffffff) {
+ FAIL("Numeric literal out of range");
+ }
+ info->kind = VarKind::kLocal;
+ info->type = AsmType::Float();
+ info->index = static_cast<uint32_t>(param_count + locals->size());
+ locals->push_back(kWasmF32);
+ int32_t value = static_cast<int32_t>(uvalue);
+ if (negate) {
+ value = -value;
+ }
+ double fvalue = static_cast<double>(value);
+ byte code[] = {WASM_F32(fvalue)};
+ current_function_builder_->EmitCode(code, sizeof(code));
+ current_function_builder_->EmitSetLocal(info->index);
+ } else {
+ FAIL("Expected variable initial value");
+ }
+ EXPECT_TOKEN(')');
+ } else {
+ FAIL("expected fround or const global");
+ }
+ } else if (CheckForDouble(&dvalue)) {
+ info->kind = VarKind::kLocal;
+ info->type = AsmType::Double();
+ info->index = static_cast<uint32_t>(param_count + locals->size());
+ locals->push_back(kWasmF64);
+ byte code[] = {WASM_F64(dvalue)};
+ current_function_builder_->EmitCode(code, sizeof(code));
+ current_function_builder_->EmitSetLocal(info->index);
+ } else if (CheckForUnsigned(&uvalue)) {
+ info->kind = VarKind::kLocal;
+ info->type = AsmType::Int();
+ info->index = static_cast<uint32_t>(param_count + locals->size());
+ locals->push_back(kWasmI32);
+ int32_t value = static_cast<int32_t>(uvalue);
+ current_function_builder_->EmitI32Const(value);
+ current_function_builder_->EmitSetLocal(info->index);
+ } else {
+ FAIL("Expected variable initial value");
+ }
+ if (!Peek(',')) {
+ break;
+ }
+ scanner_.EnterLocalScope();
+ EXPECT_TOKEN(',');
+ scanner_.EnterGlobalScope();
+ }
+ SkipSemicolon();
+ }
+}
+
+// ValidateStatement
+void AsmJsParser::ValidateStatement() {
+ call_coercion_ = nullptr;
+ if (Peek('{')) {
+ RECURSE(Block());
+ } else if (Peek(';')) {
+ RECURSE(EmptyStatement());
+ } else if (Peek(TOK(if))) {
+ RECURSE(IfStatement());
+ // clang-format off
+ } else if (Peek(TOK(return))) {
+ // clang-format on
+ RECURSE(ReturnStatement());
+ } else if (IterationStatement()) {
+ // Handled in IterationStatement.
+ } else if (Peek(TOK(break))) {
+ RECURSE(BreakStatement());
+ } else if (Peek(TOK(continue))) {
+ RECURSE(ContinueStatement());
+ } else if (Peek(TOK(switch))) {
+ RECURSE(SwitchStatement());
+ } else {
+ RECURSE(ExpressionStatement());
+ }
+}
+
+// 6.5.1 Block
+void AsmJsParser::Block() {
+ bool can_break_to_block = pending_label_ != 0;
+ if (can_break_to_block) {
+ Begin(pending_label_);
+ }
+ pending_label_ = 0;
+ EXPECT_TOKEN('{');
+ while (!failed_ && !Peek('}')) {
+ RECURSE(ValidateStatement());
+ }
+ EXPECT_TOKEN('}');
+ if (can_break_to_block) {
+ End();
+ }
+}
+
+// 6.5.2 ExpressionStatement
+void AsmJsParser::ExpressionStatement() {
+ if (scanner_.IsGlobal() || scanner_.IsLocal()) {
+ // NOTE: Both global or local identifiers can also be used as labels.
+ scanner_.Next();
+ if (Peek(':')) {
+ scanner_.Rewind();
+ RECURSE(LabelledStatement());
+ return;
+ }
+ scanner_.Rewind();
+ }
+ AsmType* ret;
+ RECURSE(ret = ValidateExpression());
+ if (!ret->IsA(AsmType::Void())) {
+ current_function_builder_->Emit(kExprDrop);
+ }
+ SkipSemicolon();
+}
+
+// 6.5.3 EmptyStatement
+void AsmJsParser::EmptyStatement() { EXPECT_TOKEN(';'); }
+
+// 6.5.4 IfStatement
+void AsmJsParser::IfStatement() {
+ EXPECT_TOKEN(TOK(if));
+ EXPECT_TOKEN('(');
+ RECURSE(Expression(AsmType::Int()));
+ EXPECT_TOKEN(')');
+ current_function_builder_->EmitWithU8(kExprIf, kLocalVoid);
+ BareBegin();
+ RECURSE(ValidateStatement());
+ if (Check(TOK(else))) {
+ current_function_builder_->Emit(kExprElse);
+ RECURSE(ValidateStatement());
+ }
+ current_function_builder_->Emit(kExprEnd);
+ BareEnd();
+}
+
+// 6.5.5 ReturnStatement
+void AsmJsParser::ReturnStatement() {
+ // clang-format off
+ EXPECT_TOKEN(TOK(return ));
+ // clang-format on
+ if (!Peek(';') && !Peek('}')) {
+ // TODO(bradnelson): See if this can be factored out.
+ AsmType* ret;
+ RECURSE(ret = Expression(return_type_));
+ if (ret->IsA(AsmType::Double())) {
+ return_type_ = AsmType::Double();
+ } else if (ret->IsA(AsmType::Float())) {
+ return_type_ = AsmType::Float();
+ } else if (ret->IsA(AsmType::Signed())) {
+ return_type_ = AsmType::Signed();
+ } else {
+ FAIL("Invalid return type");
+ }
+ } else {
+ return_type_ = AsmType::Void();
+ }
+ current_function_builder_->Emit(kExprReturn);
+ SkipSemicolon();
+}
+
+// 6.5.6 IterationStatement
+bool AsmJsParser::IterationStatement() {
+ if (Peek(TOK(while))) {
+ WhileStatement();
+ } else if (Peek(TOK(do))) {
+ DoStatement();
+ } else if (Peek(TOK(for))) {
+ ForStatement();
+ } else {
+ return false;
+ }
+ return true;
+}
+
+// 6.5.6 IterationStatement - while
+void AsmJsParser::WhileStatement() {
+ // a: block {
+ Begin(pending_label_);
+ // b: loop {
+ Loop(pending_label_);
+ pending_label_ = 0;
+ EXPECT_TOKEN(TOK(while));
+ EXPECT_TOKEN('(');
+ RECURSE(Expression(AsmType::Int()));
+ EXPECT_TOKEN(')');
+ // if (!CONDITION) break a;
+ current_function_builder_->Emit(kExprI32Eqz);
+ current_function_builder_->EmitWithU8(kExprBrIf, 1);
+ // BODY
+ RECURSE(ValidateStatement());
+ // continue b;
+ current_function_builder_->EmitWithU8(kExprBr, 0);
+ End();
+ // }
+ // }
+ End();
+}
+
+// 6.5.6 IterationStatement - do
+void AsmJsParser::DoStatement() {
+ // a: block {
+ Begin(pending_label_);
+ // b: loop {
+ Loop();
+ // c: block { // but treated like loop so continue works
+ BareBegin(BlockKind::kLoop, pending_label_);
+ current_function_builder_->EmitWithU8(kExprBlock, kLocalVoid);
+ pending_label_ = 0;
+ EXPECT_TOKEN(TOK(do));
+ // BODY
+ RECURSE(ValidateStatement());
+ EXPECT_TOKEN(TOK(while));
+ End();
+ // }
+ EXPECT_TOKEN('(');
+ RECURSE(Expression(AsmType::Int()));
+ // if (CONDITION) break a;
+ current_function_builder_->Emit(kExprI32Eqz);
+ current_function_builder_->EmitWithU8(kExprBrIf, 1);
+ // continue b;
+ current_function_builder_->EmitWithU8(kExprBr, 0);
+ EXPECT_TOKEN(')');
+ // }
+ End();
+ // }
+ End();
+ SkipSemicolon();
+}
+
+// 6.5.6 IterationStatement - for
+void AsmJsParser::ForStatement() {
+ EXPECT_TOKEN(TOK(for));
+ EXPECT_TOKEN('(');
+ if (!Peek(';')) {
+ Expression(nullptr);
+ }
+ EXPECT_TOKEN(';');
+ // a: block {
+ Begin(pending_label_);
+ // b: loop {
+ Loop(pending_label_);
+ pending_label_ = 0;
+ if (!Peek(';')) {
+ // if (CONDITION) break a;
+ RECURSE(Expression(AsmType::Int()));
+ current_function_builder_->Emit(kExprI32Eqz);
+ current_function_builder_->EmitWithU8(kExprBrIf, 1);
+ }
+ EXPECT_TOKEN(';');
+ // Stash away INCREMENT
+ size_t increment_position = current_function_builder_->GetPosition();
+ if (!Peek(')')) {
+ RECURSE(Expression(nullptr));
+ }
+ std::vector<byte> increment_code;
+ current_function_builder_->StashCode(&increment_code, increment_position);
+ EXPECT_TOKEN(')');
+ // BODY
+ RECURSE(ValidateStatement());
+ // INCREMENT
+ current_function_builder_->EmitCode(
+ increment_code.data(), static_cast<uint32_t>(increment_code.size()));
+ current_function_builder_->EmitWithU8(kExprBr, 0);
+ // }
+ End();
+ // }
+ End();
+}
+
+// 6.5.7 BreakStatement
+void AsmJsParser::BreakStatement() {
+ EXPECT_TOKEN(TOK(break));
+ AsmJsScanner::token_t label_name = kTokenNone;
+ if (scanner_.IsGlobal() || scanner_.IsLocal()) {
+ // NOTE: Currently using globals/locals for labels too.
+ label_name = Consume();
+ }
+ int depth = FindBreakLabelDepth(label_name);
+ if (depth < 0) {
+ FAIL("Illegal break");
+ }
+ current_function_builder_->Emit(kExprBr);
+ current_function_builder_->EmitVarInt(depth);
+ SkipSemicolon();
+}
+
+// 6.5.8 ContinueStatement
+void AsmJsParser::ContinueStatement() {
+ EXPECT_TOKEN(TOK(continue));
+ AsmJsScanner::token_t label_name = kTokenNone;
+ if (scanner_.IsGlobal() || scanner_.IsLocal()) {
+ // NOTE: Currently using globals/locals for labels too.
+ label_name = Consume();
+ }
+ int depth = FindContinueLabelDepth(label_name);
+ if (depth < 0) {
+ FAIL("Illegal continue");
+ }
+ current_function_builder_->Emit(kExprBr);
+ current_function_builder_->EmitVarInt(depth);
+ SkipSemicolon();
+}
+
+// 6.5.9 LabelledStatement
+void AsmJsParser::LabelledStatement() {
+ DCHECK(scanner_.IsGlobal() || scanner_.IsLocal());
+ // NOTE: Currently using globals/locals for labels too.
+ if (pending_label_ != 0) {
+ FAIL("Double label unsupported");
+ }
+ pending_label_ = scanner_.Token();
+ scanner_.Next();
+ EXPECT_TOKEN(':');
+ RECURSE(ValidateStatement());
+}
+
+// 6.5.10 SwitchStatement
+void AsmJsParser::SwitchStatement() {
+ EXPECT_TOKEN(TOK(switch));
+ EXPECT_TOKEN('(');
+ AsmType* test;
+ RECURSE(test = Expression(nullptr));
+ if (!test->IsA(AsmType::Signed())) {
+ FAIL("Expected signed for switch value");
+ }
+ EXPECT_TOKEN(')');
+ uint32_t tmp = TempVariable(0);
+ current_function_builder_->EmitSetLocal(tmp);
+ Begin(pending_label_);
+ pending_label_ = 0;
+ // TODO(bradnelson): Make less weird.
+ std::vector<int32_t> cases;
+ GatherCases(&cases); // Skips { implicitly.
+ size_t count = cases.size() + 1;
+ for (size_t i = 0; i < count; ++i) {
+ BareBegin(BlockKind::kOther);
+ current_function_builder_->EmitWithU8(kExprBlock, kLocalVoid);
+ }
+ int table_pos = 0;
+ for (auto c : cases) {
+ current_function_builder_->EmitGetLocal(tmp);
+ current_function_builder_->EmitI32Const(c);
+ current_function_builder_->Emit(kExprI32Eq);
+ current_function_builder_->EmitWithVarInt(kExprBrIf, table_pos++);
+ }
+ current_function_builder_->EmitWithVarInt(kExprBr, table_pos++);
+ while (!failed_ && Peek(TOK(case))) {
+ current_function_builder_->Emit(kExprEnd);
+ BareEnd();
+ RECURSE(ValidateCase());
+ }
+ current_function_builder_->Emit(kExprEnd);
+ BareEnd();
+ if (Peek(TOK(default))) {
+ RECURSE(ValidateDefault());
+ }
+ EXPECT_TOKEN('}');
+ End();
+}
+
+// 6.6. ValidateCase
+void AsmJsParser::ValidateCase() {
+ EXPECT_TOKEN(TOK(case));
+ bool negate = false;
+ if (Check('-')) {
+ negate = true;
+ }
+ uint64_t uvalue;
+ if (!CheckForUnsigned(&uvalue)) {
+ FAIL("Expected numeric literal");
+ }
+ // TODO(bradnelson): Share negation plumbing.
+ if ((negate && uvalue > 0x80000000) || (!negate && uvalue > 0x7fffffff)) {
+ FAIL("Numeric literal out of range");
+ }
+ int32_t value = static_cast<int32_t>(uvalue);
+ if (negate) {
+ value = -value;
+ }
+ EXPECT_TOKEN(':');
+ while (!failed_ && !Peek('}') && !Peek(TOK(case)) && !Peek(TOK(default))) {
+ RECURSE(ValidateStatement());
+ }
+}
+
+// 6.7 ValidateDefault
+void AsmJsParser::ValidateDefault() {
+ EXPECT_TOKEN(TOK(default));
+ EXPECT_TOKEN(':');
+ while (!failed_ && !Peek('}')) {
+ RECURSE(ValidateStatement());
+ }
+}
+
+// 6.8 ValidateExpression
+AsmType* AsmJsParser::ValidateExpression() {
+ AsmType* ret;
+ RECURSEn(ret = Expression(nullptr));
+ return ret;
+}
+
+// 6.8.1 Expression
+AsmType* AsmJsParser::Expression(AsmType* expected) {
+ AsmType* a;
+ for (;;) {
+ RECURSEn(a = AssignmentExpression());
+ if (Peek(',')) {
+ if (a->IsA(AsmType::None())) {
+ FAILn("Expected actual type");
+ }
+ if (!a->IsA(AsmType::Void())) {
+ current_function_builder_->Emit(kExprDrop);
+ }
+ EXPECT_TOKENn(',');
+ continue;
+ }
+ break;
+ }
+ if (expected != nullptr && !a->IsA(expected)) {
+ FAILn("Unexpected type");
+ }
+ return a;
+}
+
+// 6.8.2 NumericLiteral
+AsmType* AsmJsParser::NumericLiteral() {
+ call_coercion_ = nullptr;
+ double dvalue = 0.0;
+ uint64_t uvalue = 0;
+ if (CheckForDouble(&dvalue)) {
+ byte code[] = {WASM_F64(dvalue)};
+ current_function_builder_->EmitCode(code, sizeof(code));
+ return AsmType::Double();
+ } else if (CheckForUnsigned(&uvalue)) {
+ if (uvalue <= 0x7fffffff) {
+ current_function_builder_->EmitI32Const(static_cast<int32_t>(uvalue));
+ return AsmType::FixNum();
+ } else if (uvalue <= 0xffffffff) {
+ current_function_builder_->EmitI32Const(static_cast<int32_t>(uvalue));
+ return AsmType::Unsigned();
+ } else {
+ FAILn("Integer numeric literal out of range.");
+ }
+ } else {
+ FAILn("Expected numeric literal.");
+ }
+}
+
+// 6.8.3 Identifier
+AsmType* AsmJsParser::Identifier() {
+ call_coercion_ = nullptr;
+ if (scanner_.IsLocal()) {
+ VarInfo* info = GetVarInfo(Consume());
+ if (info->kind != VarKind::kLocal) {
+ FAILn("Undefined local variable");
+ }
+ current_function_builder_->EmitGetLocal(info->index);
+ return info->type;
+ } else if (scanner_.IsGlobal()) {
+ VarInfo* info = GetVarInfo(Consume());
+ if (info->kind != VarKind::kGlobal) {
+ FAILn("Undefined global variable");
+ }
+ current_function_builder_->EmitWithVarInt(kExprGetGlobal, VarIndex(info));
+ return info->type;
+ }
+ UNREACHABLE();
+ return nullptr;
+}
+
+// 6.8.4 CallExpression
+AsmType* AsmJsParser::CallExpression() {
+ AsmType* ret;
+ if (scanner_.IsGlobal() &&
+ GetVarInfo(scanner_.Token())->type->IsA(stdlib_fround_)) {
+ ValidateFloatCoercion();
+ return AsmType::Float();
+ } else if (scanner_.IsGlobal() &&
+ GetVarInfo(scanner_.Token())->type->IsA(AsmType::Heap())) {
+ RECURSEn(ret = MemberExpression());
+ } else if (Peek('(')) {
+ RECURSEn(ret = ParenthesizedExpression());
+ } else if (PeekCall()) {
+ RECURSEn(ret = ValidateCall());
+ } else if (scanner_.IsLocal() || scanner_.IsGlobal()) {
+ RECURSEn(ret = Identifier());
+ } else {
+ RECURSEn(ret = NumericLiteral());
+ }
+ return ret;
+}
+
+// 6.8.5 MemberExpression
+AsmType* AsmJsParser::MemberExpression() {
+ call_coercion_ = nullptr;
+ ValidateHeapAccess();
+ if (Peek('=')) {
+ inside_heap_assignment_ = true;
+ return heap_access_type_->StoreType();
+ } else {
+#define V(array_type, wasmload, wasmstore, type) \
+ if (heap_access_type_->IsA(AsmType::array_type())) { \
+ current_function_builder_->Emit(kExpr##type##AsmjsLoad##wasmload); \
+ return heap_access_type_->LoadType(); \
+ }
+ STDLIB_ARRAY_TYPE_LIST(V)
+#undef V
+ FAILn("Expected valid heap load");
+ }
+}
+
+// 6.8.6 AssignmentExpression
+AsmType* AsmJsParser::AssignmentExpression() {
+ AsmType* ret;
+ if (scanner_.IsGlobal() &&
+ GetVarInfo(scanner_.Token())->type->IsA(AsmType::Heap())) {
+ RECURSEn(ret = ConditionalExpression());
+ if (Peek('=')) {
+ if (!inside_heap_assignment_) {
+ FAILn("Invalid assignment target");
+ }
+ inside_heap_assignment_ = false;
+ AsmType* heap_type = heap_access_type_;
+ EXPECT_TOKENn('=');
+ AsmType* value;
+ RECURSEn(value = AssignmentExpression());
+ if (!value->IsA(ret)) {
+ FAILn("Illegal type stored to heap view");
+ }
+ if (heap_type->IsA(AsmType::Float32Array()) &&
+ value->IsA(AsmType::Double())) {
+ // Assignment to a float32 heap can be used to convert doubles.
+ current_function_builder_->Emit(kExprF32ConvertF64);
+ }
+ ret = value;
+#define V(array_type, wasmload, wasmstore, type) \
+ if (heap_type->IsA(AsmType::array_type())) { \
+ current_function_builder_->Emit(kExpr##type##AsmjsStore##wasmstore); \
+ return ret; \
+ }
+ STDLIB_ARRAY_TYPE_LIST(V)
+#undef V
+ }
+ } else if (scanner_.IsLocal() || scanner_.IsGlobal()) {
+ bool is_local = scanner_.IsLocal();
+ VarInfo* info = GetVarInfo(scanner_.Token());
+ USE(is_local);
+ ret = info->type;
+ scanner_.Next();
+ if (Check('=')) {
+ // NOTE: Before this point, this might have been VarKind::kUnused even in
+ // valid code, as it might be a label.
+ if (info->kind == VarKind::kUnused) {
+ FAILn("Undeclared assignment target");
+ }
+ DCHECK(is_local ? info->kind == VarKind::kLocal
+ : info->kind == VarKind::kGlobal);
+ AsmType* value;
+ RECURSEn(value = AssignmentExpression());
+ if (!value->IsA(ret)) {
+ FAILn("Type mismatch in assignment");
+ }
+ if (info->kind == VarKind::kLocal) {
+ current_function_builder_->EmitTeeLocal(info->index);
+ } else if (info->kind == VarKind::kGlobal) {
+ current_function_builder_->EmitWithVarUint(kExprSetGlobal,
+ VarIndex(info));
+ current_function_builder_->EmitWithVarUint(kExprGetGlobal,
+ VarIndex(info));
+ } else {
+ UNREACHABLE();
+ }
+ return ret;
+ }
+ scanner_.Rewind();
+ RECURSEn(ret = ConditionalExpression());
+ } else {
+ RECURSEn(ret = ConditionalExpression());
+ }
+ return ret;
+}
+
+// 6.8.7 UnaryExpression
+AsmType* AsmJsParser::UnaryExpression() {
+ AsmType* ret;
+ if (Check('-')) {
+ uint64_t uvalue;
+ if (CheckForUnsigned(&uvalue)) {
+ // TODO(bradnelson): was supposed to be 0x7fffffff, check errata.
+ if (uvalue <= 0x80000000) {
+ current_function_builder_->EmitI32Const(-static_cast<int32_t>(uvalue));
+ } else {
+ FAILn("Integer numeric literal out of range.");
+ }
+ ret = AsmType::Signed();
+ } else {
+ RECURSEn(ret = UnaryExpression());
+ if (ret->IsA(AsmType::Int())) {
+ TemporaryVariableScope tmp(this);
+ current_function_builder_->EmitSetLocal(tmp.get());
+ current_function_builder_->EmitI32Const(0);
+ current_function_builder_->EmitGetLocal(tmp.get());
+ current_function_builder_->Emit(kExprI32Sub);
+ ret = AsmType::Intish();
+ } else if (ret->IsA(AsmType::DoubleQ())) {
+ current_function_builder_->Emit(kExprF64Neg);
+ ret = AsmType::Double();
+ } else if (ret->IsA(AsmType::FloatQ())) {
+ current_function_builder_->Emit(kExprF32Neg);
+ ret = AsmType::Floatish();
+ } else {
+ FAILn("expected int/double?/float?");
+ }
+ }
+ } else if (Peek('+')) {
+ call_coercion_ = AsmType::Double();
+ call_coercion_position_ = scanner_.Position();
+ scanner_.Next(); // Done late for correct position.
+ RECURSEn(ret = UnaryExpression());
+ // TODO(bradnelson): Generalize.
+ if (ret->IsA(AsmType::Signed())) {
+ current_function_builder_->Emit(kExprF64SConvertI32);
+ ret = AsmType::Double();
+ } else if (ret->IsA(AsmType::Unsigned())) {
+ current_function_builder_->Emit(kExprF64UConvertI32);
+ ret = AsmType::Double();
+ } else if (ret->IsA(AsmType::DoubleQ())) {
+ ret = AsmType::Double();
+ } else if (ret->IsA(AsmType::FloatQ())) {
+ current_function_builder_->Emit(kExprF64ConvertF32);
+ ret = AsmType::Double();
+ } else {
+ FAILn("expected signed/unsigned/double?/float?");
+ }
+ } else if (Check('!')) {
+ RECURSEn(ret = UnaryExpression());
+ if (!ret->IsA(AsmType::Int())) {
+ FAILn("expected int");
+ }
+ current_function_builder_->Emit(kExprI32Eqz);
+ } else if (Check('~')) {
+ if (Check('~')) {
+ RECURSEn(ret = UnaryExpression());
+ if (ret->IsA(AsmType::Double())) {
+ current_function_builder_->Emit(kExprI32AsmjsSConvertF64);
+ } else if (ret->IsA(AsmType::FloatQ())) {
+ current_function_builder_->Emit(kExprI32AsmjsSConvertF32);
+ } else {
+ FAILn("expected double or float?");
+ }
+ ret = AsmType::Signed();
+ } else {
+ RECURSEn(ret = UnaryExpression());
+ if (!ret->IsA(AsmType::Intish())) {
+ FAILn("operator ~ expects intish");
+ }
+ current_function_builder_->EmitI32Const(0xffffffff);
+ current_function_builder_->Emit(kExprI32Xor);
+ ret = AsmType::Signed();
+ }
+ } else {
+ RECURSEn(ret = CallExpression());
+ }
+ return ret;
+}
+
+// 6.8.8 MultaplicativeExpression
+AsmType* AsmJsParser::MultiplicativeExpression() {
+ uint64_t uvalue;
+ if (CheckForUnsignedBelow(0x100000, &uvalue)) {
+ if (Check('*')) {
+ AsmType* a;
+ RECURSEn(a = UnaryExpression());
+ if (!a->IsA(AsmType::Int())) {
+ FAILn("Expected int");
+ }
+ current_function_builder_->EmitI32Const(static_cast<int32_t>(uvalue));
+ current_function_builder_->Emit(kExprI32Mul);
+ return AsmType::Intish();
+ }
+ scanner_.Rewind();
+ } else if (Check('-')) {
+ if (CheckForUnsignedBelow(0x100000, &uvalue)) {
+ current_function_builder_->EmitI32Const(-static_cast<int32_t>(uvalue));
+ if (Check('*')) {
+ AsmType* a;
+ RECURSEn(a = UnaryExpression());
+ if (!a->IsA(AsmType::Int())) {
+ FAILn("Expected int");
+ }
+ current_function_builder_->Emit(kExprI32Mul);
+ return AsmType::Intish();
+ }
+ return AsmType::Signed();
+ }
+ scanner_.Rewind();
+ }
+ AsmType* a;
+ RECURSEn(a = UnaryExpression());
+ for (;;) {
+ if (Check('*')) {
+ uint64_t uvalue;
+ if (Check('-')) {
+ if (CheckForUnsigned(&uvalue)) {
+ if (uvalue >= 0x100000) {
+ FAILn("Constant multiple out of range");
+ }
+ if (!a->IsA(AsmType::Int())) {
+ FAILn("Integer multiply of expects int");
+ }
+ current_function_builder_->EmitI32Const(static_cast<int32_t>(uvalue));
+ current_function_builder_->Emit(kExprI32Mul);
+ return AsmType::Intish();
+ }
+ scanner_.Rewind();
+ } else if (CheckForUnsigned(&uvalue)) {
+ if (uvalue >= 0x100000) {
+ FAILn("Constant multiple out of range");
+ }
+ if (!a->IsA(AsmType::Int())) {
+ FAILn("Integer multiply of expects int");
+ }
+ current_function_builder_->EmitI32Const(static_cast<int32_t>(uvalue));
+ current_function_builder_->Emit(kExprI32Mul);
+ return AsmType::Intish();
+ }
+ AsmType* b;
+ RECURSEn(b = UnaryExpression());
+ if (a->IsA(AsmType::DoubleQ()) && b->IsA(AsmType::DoubleQ())) {
+ current_function_builder_->Emit(kExprF64Mul);
+ a = AsmType::Double();
+ } else if (a->IsA(AsmType::FloatQ()) && b->IsA(AsmType::FloatQ())) {
+ current_function_builder_->Emit(kExprF32Mul);
+ a = AsmType::Floatish();
+ } else {
+ FAILn("expected doubles or floats");
+ }
+ } else if (Check('/')) {
+ AsmType* b;
+ RECURSEn(b = MultiplicativeExpression());
+ if (a->IsA(AsmType::DoubleQ()) && b->IsA(AsmType::DoubleQ())) {
+ current_function_builder_->Emit(kExprF64Div);
+ a = AsmType::Double();
+ } else if (a->IsA(AsmType::FloatQ()) && b->IsA(AsmType::FloatQ())) {
+ current_function_builder_->Emit(kExprF32Div);
+ a = AsmType::Floatish();
+ } else if (a->IsA(AsmType::Signed()) && b->IsA(AsmType::Signed())) {
+ current_function_builder_->Emit(kExprI32AsmjsDivS);
+ a = AsmType::Intish();
+ } else if (a->IsA(AsmType::Unsigned()) && b->IsA(AsmType::Unsigned())) {
+ current_function_builder_->Emit(kExprI32AsmjsDivU);
+ a = AsmType::Intish();
+ } else {
+ FAILn("expected doubles or floats");
+ }
+ } else if (Check('%')) {
+ AsmType* b;
+ RECURSEn(b = MultiplicativeExpression());
+ if (a->IsA(AsmType::DoubleQ()) && b->IsA(AsmType::DoubleQ())) {
+ current_function_builder_->Emit(kExprF64Mod);
+ a = AsmType::Double();
+ } else if (a->IsA(AsmType::Signed()) && b->IsA(AsmType::Signed())) {
+ current_function_builder_->Emit(kExprI32AsmjsRemS);
+ a = AsmType::Intish();
+ } else if (a->IsA(AsmType::Unsigned()) && b->IsA(AsmType::Unsigned())) {
+ current_function_builder_->Emit(kExprI32AsmjsRemU);
+ a = AsmType::Intish();
+ } else {
+ FAILn("expected doubles or floats");
+ }
+ } else {
+ break;
+ }
+ }
+ return a;
+}
+
+// 6.8.9 AdditiveExpression
+AsmType* AsmJsParser::AdditiveExpression() {
+ AsmType* a;
+ RECURSEn(a = MultiplicativeExpression());
+ int n = 0;
+ for (;;) {
+ if (Check('+')) {
+ AsmType* b;
+ RECURSEn(b = MultiplicativeExpression());
+ if (a->IsA(AsmType::Double()) && b->IsA(AsmType::Double())) {
+ current_function_builder_->Emit(kExprF64Add);
+ a = AsmType::Double();
+ } else if (a->IsA(AsmType::FloatQ()) && b->IsA(AsmType::FloatQ())) {
+ current_function_builder_->Emit(kExprF32Add);
+ a = AsmType::Floatish();
+ } else if (a->IsA(AsmType::Int()) && b->IsA(AsmType::Int())) {
+ current_function_builder_->Emit(kExprI32Add);
+ a = AsmType::Intish();
+ n = 2;
+ } else if (a->IsA(AsmType::Intish()) && b->IsA(AsmType::Intish())) {
+ // TODO(bradnelson): b should really only be Int.
+ // specialize intish to capture count.
+ ++n;
+ if (n > (1 << 20)) {
+ FAILn("more than 2^20 additive values");
+ }
+ current_function_builder_->Emit(kExprI32Add);
+ } else {
+ FAILn("illegal types for +");
+ }
+ } else if (Check('-')) {
+ AsmType* b;
+ RECURSEn(b = MultiplicativeExpression());
+ if (a->IsA(AsmType::Double()) && b->IsA(AsmType::Double())) {
+ current_function_builder_->Emit(kExprF64Sub);
+ a = AsmType::Double();
+ } else if (a->IsA(AsmType::FloatQ()) && b->IsA(AsmType::FloatQ())) {
+ current_function_builder_->Emit(kExprF32Sub);
+ a = AsmType::Floatish();
+ } else if (a->IsA(AsmType::Int()) && b->IsA(AsmType::Int())) {
+ current_function_builder_->Emit(kExprI32Sub);
+ a = AsmType::Intish();
+ n = 2;
+ } else if (a->IsA(AsmType::Intish()) && b->IsA(AsmType::Intish())) {
+ // TODO(bradnelson): b should really only be Int.
+ // specialize intish to capture count.
+ ++n;
+ if (n > (1 << 20)) {
+ FAILn("more than 2^20 additive values");
+ }
+ current_function_builder_->Emit(kExprI32Sub);
+ } else {
+ FAILn("illegal types for +");
+ }
+ } else {
+ break;
+ }
+ }
+ return a;
+}
+
+// 6.8.10 ShiftExpression
+AsmType* AsmJsParser::ShiftExpression() {
+ AsmType* a = nullptr;
+ RECURSEn(a = AdditiveExpression());
+ for (;;) {
+ switch (scanner_.Token()) {
+// TODO(bradnelson): Implement backtracking to avoid emitting code
+// for the x >>> 0 case (similar to what's there for |0).
+#define HANDLE_CASE(op, opcode, name, result) \
+ case TOK(op): { \
+ EXPECT_TOKENn(TOK(op)); \
+ AsmType* b = nullptr; \
+ RECURSEn(b = AdditiveExpression()); \
+ if (!(a->IsA(AsmType::Intish()) && b->IsA(AsmType::Intish()))) { \
+ FAILn("Expected intish for operator " #name "."); \
+ } \
+ current_function_builder_->Emit(kExpr##opcode); \
+ a = AsmType::result(); \
+ continue; \
+ }
+ HANDLE_CASE(SHL, I32Shl, "<<", Signed);
+ HANDLE_CASE(SAR, I32ShrS, ">>", Signed);
+ HANDLE_CASE(SHR, I32ShrU, ">>>", Unsigned);
+#undef HANDLE_CASE
+ default:
+ return a;
+ }
+ }
+}
+
+// 6.8.11 RelationalExpression
+AsmType* AsmJsParser::RelationalExpression() {
+ AsmType* a = nullptr;
+ RECURSEn(a = ShiftExpression());
+ for (;;) {
+ switch (scanner_.Token()) {
+#define HANDLE_CASE(op, sop, uop, dop, fop, name) \
+ case op: { \
+ EXPECT_TOKENn(op); \
+ AsmType* b = nullptr; \
+ RECURSEn(b = ShiftExpression()); \
+ if (a->IsA(AsmType::Signed()) && b->IsA(AsmType::Signed())) { \
+ current_function_builder_->Emit(kExpr##sop); \
+ } else if (a->IsA(AsmType::Unsigned()) && b->IsA(AsmType::Unsigned())) { \
+ current_function_builder_->Emit(kExpr##uop); \
+ } else if (a->IsA(AsmType::Double()) && b->IsA(AsmType::Double())) { \
+ current_function_builder_->Emit(kExpr##dop); \
+ } else if (a->IsA(AsmType::Float()) && b->IsA(AsmType::Float())) { \
+ current_function_builder_->Emit(kExpr##fop); \
+ } else { \
+ FAILn("Expected signed, unsigned, double, or float for operator " #name \
+ "."); \
+ } \
+ a = AsmType::Int(); \
+ continue; \
+ }
+ HANDLE_CASE('<', I32LtS, I32LtU, F64Lt, F32Lt, "<");
+ HANDLE_CASE(TOK(LE), I32LeS, I32LeU, F64Le, F32Le, "<=");
+ HANDLE_CASE('>', I32GtS, I32GtU, F64Gt, F32Gt, ">");
+ HANDLE_CASE(TOK(GE), I32GeS, I32GeU, F64Ge, F32Ge, ">=");
+#undef HANDLE_CASE
+ default:
+ return a;
+ }
+ }
+}
+
+// 6.8.12 EqualityExpression
+AsmType* AsmJsParser::EqualityExpression() {
+ AsmType* a = nullptr;
+ RECURSEn(a = RelationalExpression());
+ for (;;) {
+ switch (scanner_.Token()) {
+#define HANDLE_CASE(op, sop, uop, dop, fop, name) \
+ case op: { \
+ EXPECT_TOKENn(op); \
+ AsmType* b = nullptr; \
+ RECURSEn(b = RelationalExpression()); \
+ if (a->IsA(AsmType::Signed()) && b->IsA(AsmType::Signed())) { \
+ current_function_builder_->Emit(kExpr##sop); \
+ } else if (a->IsA(AsmType::Unsigned()) && b->IsA(AsmType::Unsigned())) { \
+ current_function_builder_->Emit(kExpr##uop); \
+ } else if (a->IsA(AsmType::Double()) && b->IsA(AsmType::Double())) { \
+ current_function_builder_->Emit(kExpr##dop); \
+ } else if (a->IsA(AsmType::Float()) && b->IsA(AsmType::Float())) { \
+ current_function_builder_->Emit(kExpr##fop); \
+ } else { \
+ FAILn("Expected signed, unsigned, double, or float for operator " #name \
+ "."); \
+ } \
+ a = AsmType::Int(); \
+ continue; \
+ }
+ HANDLE_CASE(TOK(EQ), I32Eq, I32Eq, F64Eq, F32Eq, "==");
+ HANDLE_CASE(TOK(NE), I32Ne, I32Ne, F64Ne, F32Ne, "!=");
+#undef HANDLE_CASE
+ default:
+ return a;
+ }
+ }
+}
+
+// 6.8.13 BitwiseANDExpression
+AsmType* AsmJsParser::BitwiseANDExpression() {
+ AsmType* a = nullptr;
+ RECURSEn(a = EqualityExpression());
+ while (Check('&')) {
+ AsmType* b = nullptr;
+ RECURSEn(b = EqualityExpression());
+ if (a->IsA(AsmType::Intish()) && b->IsA(AsmType::Intish())) {
+ current_function_builder_->Emit(kExprI32And);
+ a = AsmType::Signed();
+ } else {
+ FAILn("Expected intish for operator &.");
+ }
+ }
+ return a;
+}
+
+// 6.8.14 BitwiseXORExpression
+AsmType* AsmJsParser::BitwiseXORExpression() {
+ AsmType* a = nullptr;
+ RECURSEn(a = BitwiseANDExpression());
+ while (Check('^')) {
+ AsmType* b = nullptr;
+ RECURSEn(b = BitwiseANDExpression());
+ if (a->IsA(AsmType::Intish()) && b->IsA(AsmType::Intish())) {
+ current_function_builder_->Emit(kExprI32Xor);
+ a = AsmType::Signed();
+ } else {
+ FAILn("Expected intish for operator &.");
+ }
+ }
+ return a;
+}
+
+// 6.8.15 BitwiseORExpression
+AsmType* AsmJsParser::BitwiseORExpression() {
+ AsmType* a = nullptr;
+ RECURSEn(a = BitwiseXORExpression());
+ while (Check('|')) {
+ // TODO(bradnelson): Make it prettier.
+ AsmType* b = nullptr;
+ bool zero = false;
+ int old_pos;
+ size_t old_code;
+ if (CheckForZero()) {
+ old_pos = scanner_.GetPosition();
+ old_code = current_function_builder_->GetPosition();
+ scanner_.Rewind();
+ zero = true;
+ }
+ RECURSEn(b = BitwiseXORExpression());
+ // Handle |0 specially.
+ if (zero && old_pos == scanner_.GetPosition()) {
+ current_function_builder_->StashCode(nullptr, old_code);
+ a = AsmType::Signed();
+ continue;
+ }
+ if (a->IsA(AsmType::Intish()) && b->IsA(AsmType::Intish())) {
+ current_function_builder_->Emit(kExprI32Ior);
+ a = AsmType::Signed();
+ } else {
+ FAILn("Expected intish for operator |.");
+ }
+ }
+ return a;
+}
+
+// 6.8.16 ConditionalExpression
+AsmType* AsmJsParser::ConditionalExpression() {
+ AsmType* test = nullptr;
+ RECURSEn(test = BitwiseORExpression());
+ if (Check('?')) {
+ if (!test->IsA(AsmType::Int())) {
+ FAILn("Expected int in condition of ternary operator.");
+ }
+ current_function_builder_->EmitWithU8(kExprIf, kLocalI32);
+ size_t fixup = current_function_builder_->GetPosition() -
+ 1; // Assumes encoding knowledge.
+ AsmType* cons = nullptr;
+ RECURSEn(cons = AssignmentExpression());
+ current_function_builder_->Emit(kExprElse);
+ EXPECT_TOKENn(':');
+ AsmType* alt = nullptr;
+ RECURSEn(alt = AssignmentExpression());
+ current_function_builder_->Emit(kExprEnd);
+ if (cons->IsA(AsmType::Int()) && alt->IsA(AsmType::Int())) {
+ current_function_builder_->FixupByte(fixup, kLocalI32);
+ return AsmType::Int();
+ } else if (cons->IsA(AsmType::Double()) && alt->IsA(AsmType::Double())) {
+ current_function_builder_->FixupByte(fixup, kLocalF64);
+ return AsmType::Double();
+ } else if (cons->IsA(AsmType::Float()) && alt->IsA(AsmType::Float())) {
+ current_function_builder_->FixupByte(fixup, kLocalF32);
+ return AsmType::Float();
+ } else {
+ FAILn("Type mismatch in ternary operator.");
+ }
+ } else {
+ return test;
+ }
+}
+
+// 6.8.17 ParenthesiedExpression
+AsmType* AsmJsParser::ParenthesizedExpression() {
+ call_coercion_ = nullptr;
+ AsmType* ret;
+ EXPECT_TOKENn('(');
+ RECURSEn(ret = Expression(nullptr));
+ EXPECT_TOKENn(')');
+ return ret;
+}
+
+// 6.9 ValidateCall
+AsmType* AsmJsParser::ValidateCall() {
+ AsmType* return_type = call_coercion_;
+ call_coercion_ = nullptr;
+ int call_pos = static_cast<int>(scanner_.Position());
+ int to_number_pos = static_cast<int>(call_coercion_position_);
+ AsmJsScanner::token_t function_name = Consume();
+
+ // Distinguish between ordinary function calls and function table calls. In
+ // both cases we might be seeing the {function_name} for the first time and
+ // hence allocate a {VarInfo} here, all subsequent uses of the same name then
+ // need to match the information stored at this point.
+ // TODO(mstarzinger): Consider using Chromiums base::Optional instead.
+ std::unique_ptr<TemporaryVariableScope> tmp;
+ if (Check('[')) {
+ RECURSEn(EqualityExpression());
+ EXPECT_TOKENn('&');
+ uint64_t mask = 0;
+ if (!CheckForUnsigned(&mask)) {
+ FAILn("Expected mask literal");
+ }
+ if (mask > 0x7fffffff) {
+ FAILn("Expected power of 2 mask");
+ }
+ if (!base::bits::IsPowerOfTwo32(static_cast<uint32_t>(1 + mask))) {
+ FAILn("Expected power of 2 mask");
+ }
+ current_function_builder_->EmitI32Const(static_cast<uint32_t>(mask));
+ current_function_builder_->Emit(kExprI32And);
+ EXPECT_TOKENn(']');
+ VarInfo* function_info = GetVarInfo(function_name);
+ if (function_info->kind == VarKind::kUnused) {
+ function_info->kind = VarKind::kTable;
+ function_info->mask = static_cast<int32_t>(mask);
+ function_info->index = module_builder_->AllocateIndirectFunctions(
+ static_cast<uint32_t>(mask + 1));
+ } else {
+ if (function_info->kind != VarKind::kTable) {
+ FAILn("Expected call table");
+ }
+ if (function_info->mask != static_cast<int32_t>(mask)) {
+ FAILn("Mask size mismatch");
+ }
+ }
+ current_function_builder_->EmitI32Const(function_info->index);
+ current_function_builder_->Emit(kExprI32Add);
+ // We have to use a temporary for the correct order of evaluation.
+ tmp.reset(new TemporaryVariableScope(this));
+ current_function_builder_->EmitSetLocal(tmp.get()->get());
+ // The position of function table calls is after the table lookup.
+ call_pos = static_cast<int>(scanner_.Position());
+ } else {
+ VarInfo* function_info = GetVarInfo(function_name);
+ if (function_info->kind == VarKind::kUnused) {
+ function_info->kind = VarKind::kFunction;
+ function_info->function_builder = module_builder_->AddFunction();
+ function_info->index = function_info->function_builder->func_index();
+ } else {
+ if (function_info->kind != VarKind::kFunction &&
+ function_info->kind < VarKind::kImportedFunction) {
+ FAILn("Expected function as call target");
+ }
+ }
+ }
+
+ // Parse argument list and gather types.
+ std::vector<AsmType*> param_types;
+ ZoneVector<AsmType*> param_specific_types(zone());
+ EXPECT_TOKENn('(');
+ while (!failed_ && !Peek(')')) {
+ AsmType* t;
+ RECURSEn(t = AssignmentExpression());
+ param_specific_types.push_back(t);
+ if (t->IsA(AsmType::Int())) {
+ param_types.push_back(AsmType::Int());
+ } else if (t->IsA(AsmType::Float())) {
+ param_types.push_back(AsmType::Float());
+ } else if (t->IsA(AsmType::Double())) {
+ param_types.push_back(AsmType::Double());
+ } else {
+ std::string a = t->Name();
+ FAILn("Bad function argument type");
+ }
+ if (!Peek(')')) {
+ EXPECT_TOKENn(',');
+ }
+ }
+ EXPECT_TOKENn(')');
+
+ // We potentially use lookahead in order to determine the return type in case
+ // it is not yet clear from the call context.
+ // TODO(mstarzinger,6183): Several issues with look-ahead are known. Fix!
+ // TODO(bradnelson): clarify how this binds, and why only float?
+ if (Peek('|') &&
+ (return_type == nullptr || return_type->IsA(AsmType::Float()))) {
+ to_number_pos = static_cast<int>(scanner_.Position());
+ return_type = AsmType::Signed();
+ } else if (return_type == nullptr) {
+ to_number_pos = call_pos; // No conversion.
+ return_type = AsmType::Void();
+ }
+
+ // Compute function type and signature based on gathered types.
+ AsmType* function_type = AsmType::Function(zone(), return_type);
+ for (auto t : param_types) {
+ function_type->AsFunctionType()->AddArgument(t);
+ }
+ FunctionSig* sig = ConvertSignature(return_type, param_types);
+ if (sig == nullptr) {
+ FAILn("Invalid function signature");
+ }
+ uint32_t signature_index = module_builder_->AddSignature(sig);
+
+ // Emit actual function invocation depending on the kind. At this point we
+ // also determined the complete function type and can perform checking against
+ // the expected type or update the expected type in case of first occurrence.
+ // Reload {VarInfo} as table might have grown.
+ VarInfo* function_info = GetVarInfo(function_name);
+ if (function_info->kind == VarKind::kImportedFunction) {
+ for (auto t : param_specific_types) {
+ if (!t->IsA(AsmType::Extern())) {
+ FAILn("Imported function args must be type extern");
+ }
+ }
+ if (return_type->IsA(AsmType::Float())) {
+ FAILn("Imported function can't be called as float");
+ }
+ DCHECK(function_info->import != nullptr);
+ // TODO(bradnelson): Factor out.
+ uint32_t cache_index = function_info->import->cache.FindOrInsert(sig);
+ uint32_t index;
+ if (cache_index >= function_info->import->cache_index.size()) {
+ index = module_builder_->AddImport(
+ function_info->import->function_name,
+ static_cast<uint32_t>(function_info->import->function_name_size),
+ sig);
+ function_info->import->cache_index.push_back(index);
+ } else {
+ index = function_info->import->cache_index[cache_index];
+ }
+ current_function_builder_->AddAsmWasmOffset(call_pos, to_number_pos);
+ current_function_builder_->Emit(kExprCallFunction);
+ current_function_builder_->EmitVarUint(index);
+ } else if (function_info->kind > VarKind::kImportedFunction) {
+ AsmCallableType* callable = function_info->type->AsCallableType();
+ if (!callable) {
+ FAILn("Expected callable function");
+ }
+ // TODO(bradnelson): Refactor AsmType to not need this.
+ if (callable->CanBeInvokedWith(return_type, param_specific_types)) {
+ // Return type ok.
+ } else if (return_type->IsA(AsmType::Void()) &&
+ callable->CanBeInvokedWith(AsmType::Float(),
+ param_specific_types)) {
+ return_type = AsmType::Float();
+ } else if (return_type->IsA(AsmType::Void()) &&
+ callable->CanBeInvokedWith(AsmType::Double(),
+ param_specific_types)) {
+ return_type = AsmType::Double();
+ } else if (return_type->IsA(AsmType::Void()) &&
+ callable->CanBeInvokedWith(AsmType::Signed(),
+ param_specific_types)) {
+ return_type = AsmType::Signed();
+ } else {
+ FAILn("Function use doesn't match definition");
+ }
+ switch (function_info->kind) {
+#define V(name, Name, op, sig) \
+ case VarKind::kMath##Name: \
+ current_function_builder_->Emit(op); \
+ break;
+ STDLIB_MATH_FUNCTION_MONOMORPHIC_LIST(V)
+#undef V
+#define V(name, Name, op, sig) \
+ case VarKind::kMath##Name: \
+ if (param_specific_types[0]->IsA(AsmType::DoubleQ())) { \
+ current_function_builder_->Emit(kExprF64##Name); \
+ } else if (param_specific_types[0]->IsA(AsmType::FloatQ())) { \
+ current_function_builder_->Emit(kExprF32##Name); \
+ } else { \
+ UNREACHABLE(); \
+ } \
+ break;
+ STDLIB_MATH_FUNCTION_CEIL_LIKE_LIST(V)
+#undef V
+ case VarKind::kMathMin:
+ case VarKind::kMathMax:
+ if (param_specific_types[0]->IsA(AsmType::Double())) {
+ for (size_t i = 1; i < param_specific_types.size(); ++i) {
+ if (function_info->kind == VarKind::kMathMin) {
+ current_function_builder_->Emit(kExprF64Min);
+ } else {
+ current_function_builder_->Emit(kExprF64Max);
+ }
+ }
+ } else if (param_specific_types[0]->IsA(AsmType::Float())) {
+ // NOTE: Not technically part of the asm.js spec, but Firefox
+ // accepts it.
+ for (size_t i = 1; i < param_specific_types.size(); ++i) {
+ if (function_info->kind == VarKind::kMathMin) {
+ current_function_builder_->Emit(kExprF32Min);
+ } else {
+ current_function_builder_->Emit(kExprF32Max);
+ }
+ }
+ } else if (param_specific_types[0]->IsA(AsmType::Int())) {
+ TemporaryVariableScope tmp_x(this);
+ TemporaryVariableScope tmp_y(this);
+ for (size_t i = 1; i < param_specific_types.size(); ++i) {
+ current_function_builder_->EmitSetLocal(tmp_x.get());
+ current_function_builder_->EmitTeeLocal(tmp_y.get());
+ current_function_builder_->EmitGetLocal(tmp_x.get());
+ if (function_info->kind == VarKind::kMathMin) {
+ current_function_builder_->Emit(kExprI32GeS);
+ } else {
+ current_function_builder_->Emit(kExprI32LeS);
+ }
+ current_function_builder_->EmitWithU8(kExprIf, kLocalI32);
+ current_function_builder_->EmitGetLocal(tmp_x.get());
+ current_function_builder_->Emit(kExprElse);
+ current_function_builder_->EmitGetLocal(tmp_y.get());
+ current_function_builder_->Emit(kExprEnd);
+ }
+ } else {
+ UNREACHABLE();
+ }
+ break;
+
+ case VarKind::kMathAbs:
+ if (param_specific_types[0]->IsA(AsmType::Signed())) {
+ TemporaryVariableScope tmp(this);
+ current_function_builder_->EmitTeeLocal(tmp.get());
+ current_function_builder_->Emit(kExprI32Clz);
+ current_function_builder_->EmitWithU8(kExprIf, kLocalI32);
+ current_function_builder_->EmitGetLocal(tmp.get());
+ current_function_builder_->Emit(kExprElse);
+ current_function_builder_->EmitI32Const(0);
+ current_function_builder_->EmitGetLocal(tmp.get());
+ current_function_builder_->Emit(kExprI32Sub);
+ current_function_builder_->Emit(kExprEnd);
+ } else if (param_specific_types[0]->IsA(AsmType::DoubleQ())) {
+ current_function_builder_->Emit(kExprF64Abs);
+ } else if (param_specific_types[0]->IsA(AsmType::FloatQ())) {
+ current_function_builder_->Emit(kExprF32Abs);
+ } else {
+ UNREACHABLE();
+ }
+ break;
+
+ case VarKind::kMathFround:
+ if (param_specific_types[0]->IsA(AsmType::DoubleQ())) {
+ current_function_builder_->Emit(kExprF32ConvertF64);
+ } else {
+ DCHECK(param_specific_types[0]->IsA(AsmType::FloatQ()));
+ }
+ break;
+
+ default:
+ UNREACHABLE();
+ }
+ } else {
+ DCHECK(function_info->kind == VarKind::kFunction ||
+ function_info->kind == VarKind::kTable);
+ if (function_info->type->IsA(AsmType::None())) {
+ function_info->type = function_type;
+ } else {
+ AsmCallableType* callable = function_info->type->AsCallableType();
+ if (!callable ||
+ !callable->CanBeInvokedWith(return_type, param_specific_types)) {
+ FAILn("Function use doesn't match definition");
+ }
+ }
+ if (function_info->kind == VarKind::kTable) {
+ current_function_builder_->EmitGetLocal(tmp.get()->get());
+ current_function_builder_->AddAsmWasmOffset(call_pos, to_number_pos);
+ current_function_builder_->Emit(kExprCallIndirect);
+ current_function_builder_->EmitVarUint(signature_index);
+ current_function_builder_->EmitVarUint(0); // table index
+ } else {
+ current_function_builder_->AddAsmWasmOffset(call_pos, to_number_pos);
+ current_function_builder_->Emit(kExprCallFunction);
+ current_function_builder_->EmitDirectCallIndex(function_info->index);
+ }
+ }
+
+ return return_type;
+}
+
+// 6.9 ValidateCall - helper
+bool AsmJsParser::PeekCall() {
+ if (!scanner_.IsGlobal()) {
+ return false;
+ }
+ if (GetVarInfo(scanner_.Token())->kind == VarKind::kFunction) {
+ return true;
+ }
+ if (GetVarInfo(scanner_.Token())->kind >= VarKind::kImportedFunction) {
+ return true;
+ }
+ if (GetVarInfo(scanner_.Token())->kind == VarKind::kUnused ||
+ GetVarInfo(scanner_.Token())->kind == VarKind::kTable) {
+ scanner_.Next();
+ if (Peek('(') || Peek('[')) {
+ scanner_.Rewind();
+ return true;
+ }
+ scanner_.Rewind();
+ }
+ return false;
+}
+
+// 6.10 ValidateHeapAccess
+void AsmJsParser::ValidateHeapAccess() {
+ VarInfo* info = GetVarInfo(Consume());
+ int32_t size = info->type->ElementSizeInBytes();
+ EXPECT_TOKEN('[');
+ uint64_t offset;
+ if (CheckForUnsigned(&offset)) {
+ // TODO(bradnelson): Check more things.
+ if (offset > 0x7fffffff || offset * size > 0x7fffffff) {
+ FAIL("Heap access out of range");
+ }
+ if (Check(']')) {
+ current_function_builder_->EmitI32Const(
+ static_cast<uint32_t>(offset * size));
+ // NOTE: This has to happen here to work recursively.
+ heap_access_type_ = info->type;
+ return;
+ } else {
+ scanner_.Rewind();
+ }
+ }
+ AsmType* index_type;
+ if (info->type->IsA(AsmType::Int8Array()) ||
+ info->type->IsA(AsmType::Uint8Array())) {
+ RECURSE(index_type = Expression(nullptr));
+ } else {
+ RECURSE(index_type = AdditiveExpression());
+ EXPECT_TOKEN(TOK(SAR));
+ uint64_t shift;
+ if (!CheckForUnsigned(&shift)) {
+ FAIL("Expected shift of word size");
+ }
+ if (shift > 3) {
+ FAIL("Expected valid heap access shift");
+ }
+ if ((1 << shift) != size) {
+ FAIL("Expected heap access shift to match heap view");
+ }
+ // Mask bottom bits to match asm.js behavior.
+ current_function_builder_->EmitI32Const(~(size - 1));
+ current_function_builder_->Emit(kExprI32And);
+ }
+ if (!index_type->IsA(AsmType::Intish())) {
+ FAIL("Expected intish index");
+ }
+ EXPECT_TOKEN(']');
+ // NOTE: This has to happen here to work recursively.
+ heap_access_type_ = info->type;
+}
+
+// 6.11 ValidateFloatCoercion
+void AsmJsParser::ValidateFloatCoercion() {
+ if (!scanner_.IsGlobal() ||
+ !GetVarInfo(scanner_.Token())->type->IsA(stdlib_fround_)) {
+ FAIL("Expected fround");
+ }
+ scanner_.Next();
+ EXPECT_TOKEN('(');
+ call_coercion_ = AsmType::Float();
+ // NOTE: The coercion position to float is not observable from JavaScript,
+ // because imported functions are not allowed to have float return type.
+ call_coercion_position_ = scanner_.Position();
+ AsmType* ret;
+ RECURSE(ret = ValidateExpression());
+ if (ret->IsA(AsmType::Floatish())) {
+ // Do nothing, as already a float.
+ } else if (ret->IsA(AsmType::DoubleQ())) {
+ current_function_builder_->Emit(kExprF32ConvertF64);
+ } else if (ret->IsA(AsmType::Signed())) {
+ current_function_builder_->Emit(kExprF32SConvertI32);
+ } else if (ret->IsA(AsmType::Unsigned())) {
+ current_function_builder_->Emit(kExprF32UConvertI32);
+ } else {
+ FAIL("Illegal conversion to float");
+ }
+ EXPECT_TOKEN(')');
+}
+
+void AsmJsParser::GatherCases(std::vector<int32_t>* cases) {
+ int start = scanner_.GetPosition();
+ int depth = 0;
+ for (;;) {
+ if (Peek('{')) {
+ ++depth;
+ } else if (Peek('}')) {
+ --depth;
+ if (depth <= 0) {
+ break;
+ }
+ } else if (depth == 1 && Peek(TOK(case))) {
+ scanner_.Next();
+ int32_t value;
+ uint64_t uvalue;
+ if (Check('-')) {
+ if (!CheckForUnsigned(&uvalue)) {
+ break;
+ }
+ value = -static_cast<int32_t>(uvalue);
+ } else {
+ if (!CheckForUnsigned(&uvalue)) {
+ break;
+ }
+ value = static_cast<int32_t>(uvalue);
+ }
+ cases->push_back(value);
+ } else if (Peek(AsmJsScanner::kEndOfInput)) {
+ break;
+ }
+ scanner_.Next();
+ }
+ scanner_.Seek(start);
+}
+
+} // namespace wasm
+} // namespace internal
+} // namespace v8
diff --git a/deps/v8/src/asmjs/asm-parser.h b/deps/v8/src/asmjs/asm-parser.h
new file mode 100644
index 0000000000..c7f6a66352
--- /dev/null
+++ b/deps/v8/src/asmjs/asm-parser.h
@@ -0,0 +1,316 @@
+// Copyright 2017 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.
+
+#ifndef V8_ASMJS_ASM_PARSER_H_
+#define V8_ASMJS_ASM_PARSER_H_
+
+#include <list>
+#include <string>
+#include <vector>
+
+#include "src/asmjs/asm-scanner.h"
+#include "src/asmjs/asm-typer.h"
+#include "src/asmjs/asm-types.h"
+#include "src/wasm/signature-map.h"
+#include "src/wasm/wasm-module-builder.h"
+#include "src/zone/zone-containers.h"
+
+namespace v8 {
+namespace internal {
+namespace wasm {
+
+// A custom parser + validator + wasm converter for asm.js:
+// http://asmjs.org/spec/latest/
+// This parser intentionally avoids the portion of JavaScript parsing
+// that are not required to determine if code is valid asm.js code.
+// * It is mostly one pass.
+// * It bails out on unexpected input.
+// * It assumes strict ordering insofar as permitted by asm.js validation rules.
+// * It relies on a custom scanner that provides de-duped identifiers in two
+// scopes (local + module wide).
+class AsmJsParser {
+ public:
+ explicit AsmJsParser(Isolate* isolate, Zone* zone, Handle<Script> script,
+ int start, int end);
+ bool Run();
+ const char* failure_message() const { return failure_message_.c_str(); }
+ int failure_location() const { return failure_location_; }
+ WasmModuleBuilder* module_builder() { return module_builder_; }
+ const AsmTyper::StdlibSet* stdlib_uses() const { return &stdlib_uses_; }
+
+ private:
+ // clang-format off
+ enum class VarKind {
+ kUnused,
+ kLocal,
+ kGlobal,
+ kSpecial,
+ kFunction,
+ kTable,
+ kImportedFunction,
+#define V(_unused0, Name, _unused1, _unused2) kMath##Name,
+ STDLIB_MATH_FUNCTION_LIST(V)
+#undef V
+#define V(Name) kMath##Name,
+ STDLIB_MATH_VALUE_LIST(V)
+#undef V
+ };
+ // clang-format on
+
+ struct FunctionImportInfo {
+ char* function_name;
+ size_t function_name_size;
+ SignatureMap cache;
+ std::vector<uint32_t> cache_index;
+ };
+
+ struct VarInfo {
+ AsmType* type;
+ WasmFunctionBuilder* function_builder;
+ FunctionImportInfo* import;
+ int32_t mask;
+ uint32_t index;
+ VarKind kind;
+ bool mutable_variable;
+ bool function_defined;
+
+ VarInfo();
+ void DeclareGlobalImport(AsmType* type, uint32_t index);
+ void DeclareStdlibFunc(VarKind kind, AsmType* type);
+ };
+
+ struct GlobalImport {
+ char* import_name;
+ size_t import_name_size;
+ uint32_t import_index;
+ uint32_t global_index;
+ bool needs_init;
+ };
+
+ enum class BlockKind { kRegular, kLoop, kOther };
+
+ struct BlockInfo {
+ BlockKind kind;
+ AsmJsScanner::token_t label;
+ };
+
+ // Helper class to make {TempVariable} safe for nesting.
+ class TemporaryVariableScope;
+
+ Zone* zone_;
+ AsmJsScanner scanner_;
+ WasmModuleBuilder* module_builder_;
+ WasmFunctionBuilder* current_function_builder_;
+ AsmType* return_type_;
+ std::uintptr_t stack_limit_;
+ AsmTyper::StdlibSet stdlib_uses_;
+ std::list<FunctionImportInfo> function_import_info_;
+ ZoneVector<VarInfo> global_var_info_;
+ ZoneVector<VarInfo> local_var_info_;
+
+ int function_temp_locals_offset_;
+ int function_temp_locals_used_;
+ int function_temp_locals_depth_;
+
+ // Error Handling related
+ bool failed_;
+ std::string failure_message_;
+ int failure_location_;
+
+ // Module Related.
+ AsmJsScanner::token_t stdlib_name_;
+ AsmJsScanner::token_t foreign_name_;
+ AsmJsScanner::token_t heap_name_;
+
+ static const AsmJsScanner::token_t kTokenNone = 0;
+
+ // Track if parsing a heap assignment.
+ bool inside_heap_assignment_;
+ AsmType* heap_access_type_;
+
+ ZoneVector<BlockInfo> block_stack_;
+
+ // Types used for stdlib function and their set up.
+ AsmType* stdlib_dq2d_;
+ AsmType* stdlib_dqdq2d_;
+ AsmType* stdlib_fq2f_;
+ AsmType* stdlib_i2s_;
+ AsmType* stdlib_ii2s_;
+ AsmType* stdlib_minmax_;
+ AsmType* stdlib_abs_;
+ AsmType* stdlib_ceil_like_;
+ AsmType* stdlib_fround_;
+
+ // When making calls, the return type is needed to lookup signatures.
+ // For +callsite(..) or fround(callsite(..)) use this value to pass
+ // along the coercion.
+ AsmType* call_coercion_;
+
+ // The source position associated with the above {call_coercion}.
+ size_t call_coercion_position_;
+
+ // Used to track the last label we've seen so it can be matched to later
+ // statements it's attached to.
+ AsmJsScanner::token_t pending_label_;
+
+ // Global imports.
+ // NOTE: Holds the strings referenced in wasm-module-builder for imports.
+ ZoneLinkedList<GlobalImport> global_imports_;
+
+ Zone* zone() { return zone_; }
+
+ inline bool Peek(AsmJsScanner::token_t token) {
+ return scanner_.Token() == token;
+ }
+
+ inline bool Check(AsmJsScanner::token_t token) {
+ if (scanner_.Token() == token) {
+ scanner_.Next();
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ inline bool CheckForZero() {
+ if (scanner_.IsUnsigned() && scanner_.AsUnsigned() == 0) {
+ scanner_.Next();
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ inline bool CheckForDouble(double* value) {
+ if (scanner_.IsDouble()) {
+ *value = scanner_.AsDouble();
+ scanner_.Next();
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ inline bool CheckForUnsigned(uint64_t* value) {
+ if (scanner_.IsUnsigned()) {
+ *value = scanner_.AsUnsigned();
+ scanner_.Next();
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ inline bool CheckForUnsignedBelow(uint64_t limit, uint64_t* value) {
+ if (scanner_.IsUnsigned() && scanner_.AsUnsigned() < limit) {
+ *value = scanner_.AsUnsigned();
+ scanner_.Next();
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ inline AsmJsScanner::token_t Consume() {
+ AsmJsScanner::token_t ret = scanner_.Token();
+ scanner_.Next();
+ return ret;
+ }
+
+ void SkipSemicolon();
+
+ VarInfo* GetVarInfo(AsmJsScanner::token_t token);
+ uint32_t VarIndex(VarInfo* info);
+ void DeclareGlobal(VarInfo* info, bool mutable_variable, AsmType* type,
+ ValueType vtype,
+ const WasmInitExpr& init = WasmInitExpr());
+
+ // Allocates a temporary local variable. The given {index} is absolute within
+ // the function body, consider using {TemporaryVariableScope} when nesting.
+ uint32_t TempVariable(int index);
+
+ void AddGlobalImport(std::string name, AsmType* type, ValueType vtype,
+ bool mutable_variable, VarInfo* info);
+
+ // Use to set up block stack layers (including synthetic ones for if-else).
+ // Begin/Loop/End below are implemented with these plus code generation.
+ void BareBegin(BlockKind kind = BlockKind::kOther,
+ AsmJsScanner::token_t label = 0);
+ void BareEnd();
+ int FindContinueLabelDepth(AsmJsScanner::token_t label);
+ int FindBreakLabelDepth(AsmJsScanner::token_t label);
+
+ // Use to set up actual wasm blocks/loops.
+ void Begin(AsmJsScanner::token_t label = 0);
+ void Loop(AsmJsScanner::token_t label = 0);
+ void End();
+
+ void InitializeStdlibTypes();
+
+ FunctionSig* ConvertSignature(AsmType* return_type,
+ const std::vector<AsmType*>& params);
+
+ // 6.1 ValidateModule
+ void ValidateModule();
+ void ValidateModuleParameters();
+ void ValidateModuleVars();
+ void ValidateModuleVar(bool mutable_variable);
+ bool ValidateModuleVarImport(VarInfo* info, bool mutable_variable);
+ void ValidateModuleVarStdlib(VarInfo* info);
+ void ValidateModuleVarNewStdlib(VarInfo* info);
+ void ValidateModuleVarFromGlobal(VarInfo* info, bool mutable_variable);
+
+ void ValidateExport(); // 6.2 ValidateExport
+ void ValidateFunctionTable(); // 6.3 ValidateFunctionTable
+ void ValidateFunction(); // 6.4 ValidateFunction
+ void ValidateFunctionParams(std::vector<AsmType*>* params);
+ void ValidateFunctionLocals(size_t param_count,
+ std::vector<ValueType>* locals);
+ void ValidateStatement(); // ValidateStatement
+ void Block(); // 6.5.1 Block
+ void ExpressionStatement(); // 6.5.2 ExpressionStatement
+ void EmptyStatement(); // 6.5.3 EmptyStatement
+ void IfStatement(); // 6.5.4 IfStatement
+ void ReturnStatement(); // 6.5.5 ReturnStatement
+ bool IterationStatement(); // 6.5.6 IterationStatement
+ void WhileStatement(); // 6.5.6 IterationStatement - while
+ void DoStatement(); // 6.5.6 IterationStatement - do
+ void ForStatement(); // 6.5.6 IterationStatement - for
+ void BreakStatement(); // 6.5.7 BreakStatement
+ void ContinueStatement(); // 6.5.8 ContinueStatement
+ void LabelledStatement(); // 6.5.9 LabelledStatement
+ void SwitchStatement(); // 6.5.10 SwitchStatement
+ void ValidateCase(); // 6.6. ValidateCase
+ void ValidateDefault(); // 6.7 ValidateDefault
+ AsmType* ValidateExpression(); // 6.8 ValidateExpression
+ AsmType* Expression(AsmType* expect); // 6.8.1 Expression
+ AsmType* NumericLiteral(); // 6.8.2 NumericLiteral
+ AsmType* Identifier(); // 6.8.3 Identifier
+ AsmType* CallExpression(); // 6.8.4 CallExpression
+ AsmType* MemberExpression(); // 6.8.5 MemberExpression
+ AsmType* AssignmentExpression(); // 6.8.6 AssignmentExpression
+ AsmType* UnaryExpression(); // 6.8.7 UnaryExpression
+ AsmType* MultiplicativeExpression(); // 6.8.8 MultaplicativeExpression
+ AsmType* AdditiveExpression(); // 6.8.9 AdditiveExpression
+ AsmType* ShiftExpression(); // 6.8.10 ShiftExpression
+ AsmType* RelationalExpression(); // 6.8.11 RelationalExpression
+ AsmType* EqualityExpression(); // 6.8.12 EqualityExpression
+ AsmType* BitwiseANDExpression(); // 6.8.13 BitwiseANDExpression
+ AsmType* BitwiseXORExpression(); // 6.8.14 BitwiseXORExpression
+ AsmType* BitwiseORExpression(); // 6.8.15 BitwiseORExpression
+ AsmType* ConditionalExpression(); // 6.8.16 ConditionalExpression
+ AsmType* ParenthesizedExpression(); // 6.8.17 ParenthesiedExpression
+ AsmType* ValidateCall(); // 6.9 ValidateCall
+ bool PeekCall(); // 6.9 ValidateCall - helper
+ void ValidateHeapAccess(); // 6.10 ValidateHeapAccess
+ void ValidateFloatCoercion(); // 6.11 ValidateFloatCoercion
+
+ void GatherCases(std::vector<int32_t>* cases);
+};
+
+} // namespace wasm
+} // namespace internal
+} // namespace v8
+
+#endif // V8_ASMJS_ASM_PARSER_H_
diff --git a/deps/v8/src/asmjs/asm-scanner.cc b/deps/v8/src/asmjs/asm-scanner.cc
new file mode 100644
index 0000000000..5f272652f4
--- /dev/null
+++ b/deps/v8/src/asmjs/asm-scanner.cc
@@ -0,0 +1,431 @@
+// Copyright 2017 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.
+
+#include "src/asmjs/asm-scanner.h"
+
+#include "src/conversions.h"
+#include "src/flags.h"
+#include "src/parsing/scanner.h"
+#include "src/unicode-cache.h"
+
+namespace v8 {
+namespace internal {
+
+namespace {
+// Cap number of identifiers to ensure we can assign both global and
+// local ones a token id in the range of an int32_t.
+static const int kMaxIdentifierCount = 0xf000000;
+};
+
+AsmJsScanner::AsmJsScanner()
+ : token_(kUninitialized),
+ preceding_token_(kUninitialized),
+ next_token_(kUninitialized),
+ position_(0),
+ preceding_position_(0),
+ next_position_(0),
+ rewind_(false),
+ in_local_scope_(false),
+ global_count_(0),
+ double_value_(0.0),
+ unsigned_value_(0),
+ preceded_by_newline_(false) {
+#define V(name, _junk1, _junk2, _junk3) property_names_[#name] = kToken_##name;
+ STDLIB_MATH_FUNCTION_LIST(V)
+ STDLIB_ARRAY_TYPE_LIST(V)
+#undef V
+#define V(name) property_names_[#name] = kToken_##name;
+ STDLIB_MATH_VALUE_LIST(V)
+ STDLIB_OTHER_LIST(V)
+#undef V
+#define V(name) global_names_[#name] = kToken_##name;
+ KEYWORD_NAME_LIST(V)
+#undef V
+}
+
+void AsmJsScanner::SetStream(std::unique_ptr<Utf16CharacterStream> stream) {
+ stream_ = std::move(stream);
+ Next();
+}
+
+void AsmJsScanner::Next() {
+ if (rewind_) {
+ preceding_token_ = token_;
+ preceding_position_ = position_;
+ token_ = next_token_;
+ position_ = next_position_;
+ next_token_ = kUninitialized;
+ next_position_ = 0;
+ rewind_ = false;
+ return;
+ }
+
+ if (token_ == kEndOfInput || token_ == kParseError) {
+ return;
+ }
+
+#if DEBUG
+ if (FLAG_trace_asm_scanner) {
+ if (Token() == kDouble) {
+ PrintF("%lf ", AsDouble());
+ } else if (Token() == kUnsigned) {
+ PrintF("%" PRIu64 " ", AsUnsigned());
+ } else {
+ std::string name = Name(Token());
+ PrintF("%s ", name.c_str());
+ }
+ }
+#endif
+
+ preceded_by_newline_ = false;
+ preceding_token_ = token_;
+ preceding_position_ = position_;
+
+ for (;;) {
+ position_ = stream_->pos();
+ uc32 ch = stream_->Advance();
+ switch (ch) {
+ case ' ':
+ case '\t':
+ case '\r':
+ // Ignore whitespace.
+ break;
+
+ case '\n':
+ // Track when we've passed a newline for optional semicolon support,
+ // but keep scanning.
+ preceded_by_newline_ = true;
+ break;
+
+ case kEndOfInput:
+ token_ = kEndOfInput;
+ return;
+
+ case '\'':
+ case '"':
+ ConsumeString(ch);
+ return;
+
+ case '/':
+ ch = stream_->Advance();
+ if (ch == '/') {
+ ConsumeCPPComment();
+ } else if (ch == '*') {
+ if (!ConsumeCComment()) {
+ token_ = kParseError;
+ return;
+ }
+ } else {
+ stream_->Back();
+ token_ = '/';
+ return;
+ }
+ // Breaks out of switch, but loops again (i.e. the case when we parsed
+ // a comment, but need to continue to look for the next token).
+ break;
+
+ case '<':
+ case '>':
+ case '=':
+ case '!':
+ ConsumeCompareOrShift(ch);
+ return;
+
+#define V(single_char_token) case single_char_token:
+ SIMPLE_SINGLE_TOKEN_LIST(V)
+#undef V
+ // Use fixed token IDs for ASCII.
+ token_ = ch;
+ return;
+
+ default:
+ if (IsIdentifierStart(ch)) {
+ ConsumeIdentifier(ch);
+ } else if (IsNumberStart(ch)) {
+ ConsumeNumber(ch);
+ } else {
+ // TODO(bradnelson): Support unicode (probably via UnicodeCache).
+ token_ = kParseError;
+ }
+ return;
+ }
+ }
+}
+
+void AsmJsScanner::Rewind() {
+ DCHECK_NE(kUninitialized, preceding_token_);
+ // TODO(bradnelson): Currently rewinding needs to leave in place the
+ // preceding newline state (in case a |0 ends a line).
+ // This is weird and stateful, fix me.
+ DCHECK(!rewind_);
+ next_token_ = token_;
+ next_position_ = position_;
+ token_ = preceding_token_;
+ position_ = preceding_position_;
+ preceding_token_ = kUninitialized;
+ preceding_position_ = 0;
+ rewind_ = true;
+ identifier_string_.clear();
+}
+
+void AsmJsScanner::ResetLocals() { local_names_.clear(); }
+
+#if DEBUG
+// Only used for debugging.
+std::string AsmJsScanner::Name(token_t token) const {
+ if (token >= 32 && token < 127) {
+ return std::string(1, static_cast<char>(token));
+ }
+ for (auto& i : local_names_) {
+ if (i.second == token) {
+ return i.first;
+ }
+ }
+ for (auto& i : global_names_) {
+ if (i.second == token) {
+ return i.first;
+ }
+ }
+ for (auto& i : property_names_) {
+ if (i.second == token) {
+ return i.first;
+ }
+ }
+ switch (token) {
+#define V(rawname, name) \
+ case kToken_##name: \
+ return rawname;
+ LONG_SYMBOL_NAME_LIST(V)
+#undef V
+#define V(name, value, string_name) \
+ case name: \
+ return string_name;
+ SPECIAL_TOKEN_LIST(V)
+ default:
+ break;
+ }
+ UNREACHABLE();
+ return "{unreachable}";
+}
+#endif
+
+int AsmJsScanner::GetPosition() const {
+ DCHECK(!rewind_);
+ return static_cast<int>(stream_->pos());
+}
+
+void AsmJsScanner::Seek(int pos) {
+ stream_->Seek(pos);
+ preceding_token_ = kUninitialized;
+ token_ = kUninitialized;
+ next_token_ = kUninitialized;
+ preceding_position_ = 0;
+ position_ = 0;
+ next_position_ = 0;
+ rewind_ = false;
+ Next();
+}
+
+void AsmJsScanner::ConsumeIdentifier(uc32 ch) {
+ // Consume characters while still part of the identifier.
+ identifier_string_.clear();
+ while (IsIdentifierPart(ch)) {
+ identifier_string_ += ch;
+ ch = stream_->Advance();
+ }
+ // Go back one for next time.
+ stream_->Back();
+
+ // Decode what the identifier means.
+ if (preceding_token_ == '.') {
+ auto i = property_names_.find(identifier_string_);
+ if (i != property_names_.end()) {
+ token_ = i->second;
+ return;
+ }
+ } else {
+ {
+ auto i = local_names_.find(identifier_string_);
+ if (i != local_names_.end()) {
+ token_ = i->second;
+ return;
+ }
+ }
+ if (!in_local_scope_) {
+ auto i = global_names_.find(identifier_string_);
+ if (i != global_names_.end()) {
+ token_ = i->second;
+ return;
+ }
+ }
+ }
+ if (preceding_token_ == '.') {
+ CHECK(global_count_ < kMaxIdentifierCount);
+ token_ = kGlobalsStart + global_count_++;
+ property_names_[identifier_string_] = token_;
+ } else if (in_local_scope_) {
+ CHECK(local_names_.size() < kMaxIdentifierCount);
+ token_ = kLocalsStart - static_cast<token_t>(local_names_.size());
+ local_names_[identifier_string_] = token_;
+ } else {
+ CHECK(global_count_ < kMaxIdentifierCount);
+ token_ = kGlobalsStart + global_count_++;
+ global_names_[identifier_string_] = token_;
+ }
+}
+
+void AsmJsScanner::ConsumeNumber(uc32 ch) {
+ std::string number;
+ number = ch;
+ bool has_dot = ch == '.';
+ for (;;) {
+ ch = stream_->Advance();
+ if ((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') ||
+ (ch >= 'A' && ch <= 'F') || ch == '.' || ch == 'b' || ch == 'o' ||
+ ch == 'x' ||
+ ((ch == '-' || ch == '+') && (number[number.size() - 1] == 'e' ||
+ number[number.size() - 1] == 'E'))) {
+ // TODO(bradnelson): Test weird cases ending in -.
+ if (ch == '.') {
+ has_dot = true;
+ }
+ number.push_back(ch);
+ } else {
+ break;
+ }
+ }
+ stream_->Back();
+ // Special case the most common number.
+ if (number.size() == 1 && number[0] == '0') {
+ unsigned_value_ = 0;
+ token_ = kUnsigned;
+ return;
+ }
+ // Pick out dot.
+ if (number.size() == 1 && number[0] == '.') {
+ token_ = '.';
+ return;
+ }
+ // Decode numbers.
+ UnicodeCache cache;
+ double_value_ = StringToDouble(
+ &cache,
+ Vector<uint8_t>(
+ const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(number.data())),
+ static_cast<int>(number.size())),
+ ALLOW_HEX | ALLOW_OCTAL | ALLOW_BINARY | ALLOW_IMPLICIT_OCTAL);
+ if (std::isnan(double_value_)) {
+ // Check if string to number conversion didn't consume all the characters.
+ // This happens if the character filter let through something invalid
+ // like: 0123ef for example.
+ // TODO(bradnelson): Check if this happens often enough to be a perf
+ // problem.
+ if (number[0] == '.') {
+ for (size_t k = 1; k < number.size(); ++k) {
+ stream_->Back();
+ }
+ token_ = '.';
+ return;
+ }
+ // Anything else that doesn't parse is an error.
+ token_ = kParseError;
+ return;
+ }
+ if (has_dot) {
+ token_ = kDouble;
+ } else {
+ unsigned_value_ = static_cast<uint32_t>(double_value_);
+ token_ = kUnsigned;
+ }
+}
+
+bool AsmJsScanner::ConsumeCComment() {
+ for (;;) {
+ uc32 ch = stream_->Advance();
+ while (ch == '*') {
+ ch = stream_->Advance();
+ if (ch == '/') {
+ return true;
+ }
+ }
+ if (ch == kEndOfInput) {
+ return false;
+ }
+ }
+}
+
+void AsmJsScanner::ConsumeCPPComment() {
+ for (;;) {
+ uc32 ch = stream_->Advance();
+ if (ch == '\n' || ch == kEndOfInput) {
+ return;
+ }
+ }
+}
+
+void AsmJsScanner::ConsumeString(uc32 quote) {
+ // Only string allowed is 'use asm' / "use asm".
+ const char* expected = "use asm";
+ for (; *expected != '\0'; ++expected) {
+ if (stream_->Advance() != *expected) {
+ token_ = kParseError;
+ return;
+ }
+ }
+ if (stream_->Advance() != quote) {
+ token_ = kParseError;
+ return;
+ }
+ token_ = kToken_UseAsm;
+}
+
+void AsmJsScanner::ConsumeCompareOrShift(uc32 ch) {
+ uc32 next_ch = stream_->Advance();
+ if (next_ch == '=') {
+ switch (ch) {
+ case '<':
+ token_ = kToken_LE;
+ break;
+ case '>':
+ token_ = kToken_GE;
+ break;
+ case '=':
+ token_ = kToken_EQ;
+ break;
+ case '!':
+ token_ = kToken_NE;
+ break;
+ default:
+ UNREACHABLE();
+ }
+ } else if (ch == '<' && next_ch == '<') {
+ token_ = kToken_SHL;
+ } else if (ch == '>' && next_ch == '>') {
+ if (stream_->Advance() == '>') {
+ token_ = kToken_SHR;
+ } else {
+ token_ = kToken_SAR;
+ stream_->Back();
+ }
+ } else {
+ stream_->Back();
+ token_ = ch;
+ }
+}
+
+bool AsmJsScanner::IsIdentifierStart(uc32 ch) {
+ return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || ch == '_' ||
+ ch == '$';
+}
+
+bool AsmJsScanner::IsIdentifierPart(uc32 ch) {
+ return IsIdentifierStart(ch) || (ch >= '0' && ch <= '9');
+}
+
+bool AsmJsScanner::IsNumberStart(uc32 ch) {
+ return ch == '.' || (ch >= '0' && ch <= '9');
+}
+
+} // namespace internal
+} // namespace v8
diff --git a/deps/v8/src/asmjs/asm-scanner.h b/deps/v8/src/asmjs/asm-scanner.h
new file mode 100644
index 0000000000..4ac5370a1f
--- /dev/null
+++ b/deps/v8/src/asmjs/asm-scanner.h
@@ -0,0 +1,165 @@
+// Copyright 2017 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.
+
+#ifndef V8_ASMJS_ASM_SCANNER_H_
+#define V8_ASMJS_ASM_SCANNER_H_
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+
+#include "src/asmjs/asm-names.h"
+#include "src/base/logging.h"
+#include "src/globals.h"
+
+namespace v8 {
+namespace internal {
+
+class Utf16CharacterStream;
+
+// A custom scanner to extract the token stream needed to parse valid
+// asm.js: http://asmjs.org/spec/latest/
+// This scanner intentionally avoids the portion of JavaScript lexing
+// that are not required to determine if code is valid asm.js code.
+// * Strings are disallowed except for 'use asm'.
+// * Only the subset of keywords needed to check asm.js invariants are
+// included.
+// * Identifiers are accumulated into local + global string tables
+// (for performance).
+class V8_EXPORT_PRIVATE AsmJsScanner {
+ public:
+ typedef int32_t token_t;
+
+ AsmJsScanner();
+ // Pick the stream to parse (must be called before anything else).
+ void SetStream(std::unique_ptr<Utf16CharacterStream> stream);
+
+ // Get current token.
+ token_t Token() const { return token_; }
+ // Get position of current token.
+ size_t Position() const { return position_; }
+ // Advance to the next token.
+ void Next();
+ // Back up by one token.
+ void Rewind();
+ // Get raw string for current identifier.
+ const std::string& GetIdentifierString() const {
+ // Identifier strings don't work after a rewind.
+ DCHECK(!rewind_);
+ return identifier_string_;
+ }
+ // Check if we just passed a newline.
+ bool IsPrecededByNewline() const {
+ // Newline tracking doesn't work if you back up.
+ DCHECK(!rewind_);
+ return preceded_by_newline_;
+ }
+
+#if DEBUG
+ // Debug only method to go from a token back to its name.
+ // Slow, only use for debugging.
+ std::string Name(token_t token) const;
+#endif
+
+ // Get current position (to use with Seek).
+ int GetPosition() const;
+ // Restores old position (token after that position).
+ void Seek(int pos);
+
+ // Select whether identifiers are resolved in global or local scope,
+ // and which scope new identifiers are added to.
+ void EnterLocalScope() { in_local_scope_ = true; }
+ void EnterGlobalScope() { in_local_scope_ = false; }
+ // Drop all current local identifiers.
+ void ResetLocals();
+
+ // Methods to check if a token is an identifier and which scope.
+ bool IsLocal() const { return IsLocal(Token()); }
+ bool IsGlobal() const { return IsGlobal(Token()); }
+ static bool IsLocal(token_t token) { return token <= kLocalsStart; }
+ static bool IsGlobal(token_t token) { return token >= kGlobalsStart; }
+ // Methods to find the index position of an identifier (count starting from
+ // 0 for each scope separately).
+ static size_t LocalIndex(token_t token) {
+ DCHECK(IsLocal(token));
+ return -(token - kLocalsStart);
+ }
+ static size_t GlobalIndex(token_t token) {
+ DCHECK(IsGlobal(token));
+ return token - kGlobalsStart;
+ }
+
+ // Methods to check if the current token is an asm.js "number" (contains a
+ // dot) or an "unsigned" (a number without a dot).
+ bool IsUnsigned() const { return Token() == kUnsigned; }
+ uint64_t AsUnsigned() const { return unsigned_value_; }
+ bool IsDouble() const { return Token() == kDouble; }
+ double AsDouble() const { return double_value_; }
+
+ // clang-format off
+ enum {
+ // [-10000-kMaxIdentifierCount, -10000) :: Local identifiers (counting
+ // backwards)
+ // [-10000 .. -1) :: Builtin tokens like keywords
+ // (also includes some special
+ // ones like end of input)
+ // 0 .. 255 :: Single char tokens
+ // 256 .. 256+kMaxIdentifierCount :: Global identifiers
+ kLocalsStart = -10000,
+#define V(name, _junk1, _junk2, _junk3) kToken_##name,
+ STDLIB_MATH_FUNCTION_LIST(V)
+ STDLIB_ARRAY_TYPE_LIST(V)
+#undef V
+#define V(name) kToken_##name,
+ STDLIB_OTHER_LIST(V)
+ STDLIB_MATH_VALUE_LIST(V)
+ KEYWORD_NAME_LIST(V)
+#undef V
+#define V(rawname, name) kToken_##name,
+ LONG_SYMBOL_NAME_LIST(V)
+#undef V
+#define V(name, value, string_name) name = value,
+ SPECIAL_TOKEN_LIST(V)
+#undef V
+ kGlobalsStart = 256,
+ };
+ // clang-format on
+
+ private:
+ std::unique_ptr<Utf16CharacterStream> stream_;
+ token_t token_;
+ token_t preceding_token_;
+ token_t next_token_; // Only set when in {rewind} state.
+ size_t position_; // Corresponds to {token} position.
+ size_t preceding_position_; // Corresponds to {preceding_token} position.
+ size_t next_position_; // Only set when in {rewind} state.
+ bool rewind_;
+ std::string identifier_string_;
+ bool in_local_scope_;
+ std::unordered_map<std::string, token_t> local_names_;
+ std::unordered_map<std::string, token_t> global_names_;
+ std::unordered_map<std::string, token_t> property_names_;
+ int global_count_;
+ double double_value_;
+ uint64_t unsigned_value_;
+ bool preceded_by_newline_;
+
+ // Consume multiple characters.
+ void ConsumeIdentifier(uc32 ch);
+ void ConsumeNumber(uc32 ch);
+ bool ConsumeCComment();
+ void ConsumeCPPComment();
+ void ConsumeString(uc32 quote);
+ void ConsumeCompareOrShift(uc32 ch);
+
+ // Classify character categories.
+ bool IsIdentifierStart(uc32 ch);
+ bool IsIdentifierPart(uc32 ch);
+ bool IsNumberStart(uc32 ch);
+};
+
+} // namespace internal
+} // namespace v8
+
+#endif // V8_ASMJS_ASM_SCANNER_H_
diff --git a/deps/v8/src/asmjs/asm-wasm-builder.cc b/deps/v8/src/asmjs/asm-wasm-builder.cc
index 891cba3ef9..a92e3ca1b4 100644
--- a/deps/v8/src/asmjs/asm-wasm-builder.cc
+++ b/deps/v8/src/asmjs/asm-wasm-builder.cc
@@ -91,6 +91,8 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
FunctionSig::Builder b(zone(), 0, 0);
init_function_ = builder_->AddFunction(b.Build());
builder_->MarkStartFunction(init_function_);
+ // Record start of the function, used as position for the stack check.
+ init_function_->SetAsmFunctionStartPosition(literal_->start_position());
}
void BuildForeignInitFunction() {
@@ -170,7 +172,7 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
new_func_scope = new (info->zone()) DeclarationScope(
info->zone(), decl->fun()->scope()->outer_scope(), FUNCTION_SCOPE);
info->set_asm_function_scope(new_func_scope);
- if (!Compiler::ParseAndAnalyze(info.get())) {
+ if (!Compiler::ParseAndAnalyze(info.get(), info_->isolate())) {
decl->fun()->scope()->outer_scope()->RemoveInnerScope(new_func_scope);
if (isolate_->has_pending_exception()) {
isolate_->clear_pending_exception();
@@ -224,6 +226,7 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
}
RECURSE(Visit(stmt));
if (typer_failed_) break;
+ // Not stopping when a jump statement is found.
}
}
@@ -300,6 +303,8 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
void VisitGetIterator(GetIterator* expr) { UNREACHABLE(); }
+ void VisitImportCallExpression(ImportCallExpression* expr) { UNREACHABLE(); }
+
void VisitIfStatement(IfStatement* stmt) {
DCHECK_EQ(kFuncScope, scope_);
RECURSE(Visit(stmt->condition()));
@@ -1066,7 +1071,7 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
if (as_init) UnLoadInitFunction();
}
- void VisitYield(Yield* expr) { UNREACHABLE(); }
+ void VisitSuspend(Suspend* expr) { UNREACHABLE(); }
void VisitThrow(Throw* expr) { UNREACHABLE(); }
@@ -2001,6 +2006,9 @@ AsmWasmBuilder::Result AsmWasmBuilder::Run(Handle<FixedArray>* foreign_args) {
info_->parse_info()->ast_value_factory(),
info_->script(), info_->literal(), &typer_);
bool success = impl.Build();
+ if (!success) {
+ return {nullptr, nullptr, success};
+ }
*foreign_args = impl.GetForeignArgs();
ZoneBuffer* module_buffer = new (zone) ZoneBuffer(zone);
impl.builder_->WriteTo(*module_buffer);