mirror of https://github.com/lukechilds/node.git
Browse Source
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>v6
Michaël Zasso
8 years ago
1500 changed files with 110165 additions and 69314 deletions
File diff suppressed because it is too large
@ -1,58 +0,0 @@ |
|||||
// Copyright 2015 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.
|
|
||||
|
|
||||
/**
|
|
||||
* This header contains a set of experimental V8 APIs. We hope these will |
|
||||
* become a part of standard V8, but they may also be removed if we deem the |
|
||||
* experiment to not be successul. |
|
||||
*/ |
|
||||
#ifndef V8_INCLUDE_V8_EXPERIMENTAL_H_ |
|
||||
#define V8_INCLUDE_V8_EXPERIMENTAL_H_ |
|
||||
|
|
||||
#include "v8.h" // NOLINT(build/include) |
|
||||
|
|
||||
namespace v8 { |
|
||||
namespace experimental { |
|
||||
|
|
||||
// Allow the embedder to construct accessors that V8 can compile and use
|
|
||||
// directly, without jumping into the runtime.
|
|
||||
class V8_EXPORT FastAccessorBuilder { |
|
||||
public: |
|
||||
struct ValueId { |
|
||||
size_t value_id; |
|
||||
}; |
|
||||
struct LabelId { |
|
||||
size_t label_id; |
|
||||
}; |
|
||||
|
|
||||
static FastAccessorBuilder* New(Isolate* isolate); |
|
||||
|
|
||||
ValueId IntegerConstant(int int_constant); |
|
||||
ValueId GetReceiver(); |
|
||||
ValueId LoadInternalField(ValueId value_id, int field_no); |
|
||||
ValueId LoadInternalFieldUnchecked(ValueId value_id, int field_no); |
|
||||
ValueId LoadValue(ValueId value_id, int offset); |
|
||||
ValueId LoadObject(ValueId value_id, int offset); |
|
||||
ValueId ToSmi(ValueId value_id); |
|
||||
|
|
||||
void ReturnValue(ValueId value_id); |
|
||||
void CheckFlagSetOrReturnNull(ValueId value_id, int mask); |
|
||||
void CheckNotZeroOrReturnNull(ValueId value_id); |
|
||||
LabelId MakeLabel(); |
|
||||
void SetLabel(LabelId label_id); |
|
||||
void Goto(LabelId label_id); |
|
||||
void CheckNotZeroOrJump(ValueId value_id, LabelId label_id); |
|
||||
ValueId Call(v8::FunctionCallback callback, ValueId value_id); |
|
||||
|
|
||||
private: |
|
||||
FastAccessorBuilder() = delete; |
|
||||
FastAccessorBuilder(const FastAccessorBuilder&) = delete; |
|
||||
~FastAccessorBuilder() = delete; |
|
||||
void operator=(const FastAccessorBuilder&) = delete; |
|
||||
}; |
|
||||
|
|
||||
} // namespace experimental
|
|
||||
} // namespace v8
|
|
||||
|
|
||||
#endif // V8_INCLUDE_V8_EXPERIMENTAL_H_
|
|
File diff suppressed because it is too large
@ -1,4 +1,5 @@ |
|||||
per-file i18n.*=cira@chromium.org |
per-file i18n.*=cira@chromium.org |
||||
per-file i18n.*=mnita@google.com |
per-file i18n.*=mnita@google.com |
||||
|
per-file i18n.*=jshin@chromium.org |
||||
per-file typing-asm.*=aseemgarg@chromium.org |
per-file typing-asm.*=aseemgarg@chromium.org |
||||
per-file typing-asm.*=bradnelson@chromium.org |
per-file typing-asm.*=bradnelson@chromium.org |
||||
|
@ -1,139 +0,0 @@ |
|||||
// Copyright 2015 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.
|
|
||||
|
|
||||
/**
|
|
||||
* Implementation for v8-experimental.h. |
|
||||
*/ |
|
||||
|
|
||||
#include "src/api-experimental.h" |
|
||||
|
|
||||
#include "include/v8-experimental.h" |
|
||||
#include "include/v8.h" |
|
||||
#include "src/api.h" |
|
||||
#include "src/fast-accessor-assembler.h" |
|
||||
#include "src/objects-inl.h" |
|
||||
|
|
||||
namespace { |
|
||||
|
|
||||
v8::internal::FastAccessorAssembler* FromApi( |
|
||||
v8::experimental::FastAccessorBuilder* builder) { |
|
||||
return reinterpret_cast<v8::internal::FastAccessorAssembler*>(builder); |
|
||||
} |
|
||||
|
|
||||
v8::experimental::FastAccessorBuilder* FromInternal( |
|
||||
v8::internal::FastAccessorAssembler* fast_accessor_assembler) { |
|
||||
return reinterpret_cast<v8::experimental::FastAccessorBuilder*>( |
|
||||
fast_accessor_assembler); |
|
||||
} |
|
||||
|
|
||||
} // namespace
|
|
||||
|
|
||||
namespace v8 { |
|
||||
namespace internal { |
|
||||
namespace experimental { |
|
||||
|
|
||||
|
|
||||
MaybeHandle<Code> BuildCodeFromFastAccessorBuilder( |
|
||||
v8::experimental::FastAccessorBuilder* fast_handler) { |
|
||||
i::MaybeHandle<i::Code> code; |
|
||||
if (fast_handler != nullptr) { |
|
||||
auto faa = FromApi(fast_handler); |
|
||||
code = faa->Build(); |
|
||||
CHECK(!code.is_null()); |
|
||||
delete faa; |
|
||||
} |
|
||||
return code; |
|
||||
} |
|
||||
|
|
||||
} // namespace experimental
|
|
||||
} // namespace internal
|
|
||||
|
|
||||
|
|
||||
namespace experimental { |
|
||||
|
|
||||
|
|
||||
FastAccessorBuilder* FastAccessorBuilder::New(Isolate* isolate) { |
|
||||
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); |
|
||||
internal::FastAccessorAssembler* faa = |
|
||||
new internal::FastAccessorAssembler(i_isolate); |
|
||||
return FromInternal(faa); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
FastAccessorBuilder::ValueId FastAccessorBuilder::IntegerConstant( |
|
||||
int const_value) { |
|
||||
return FromApi(this)->IntegerConstant(const_value); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
FastAccessorBuilder::ValueId FastAccessorBuilder::GetReceiver() { |
|
||||
return FromApi(this)->GetReceiver(); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
FastAccessorBuilder::ValueId FastAccessorBuilder::LoadInternalField( |
|
||||
ValueId value, int field_no) { |
|
||||
return FromApi(this)->LoadInternalField(value, field_no); |
|
||||
} |
|
||||
|
|
||||
FastAccessorBuilder::ValueId FastAccessorBuilder::LoadInternalFieldUnchecked( |
|
||||
ValueId value, int field_no) { |
|
||||
return FromApi(this)->LoadInternalFieldUnchecked(value, field_no); |
|
||||
} |
|
||||
|
|
||||
FastAccessorBuilder::ValueId FastAccessorBuilder::LoadValue(ValueId value_id, |
|
||||
int offset) { |
|
||||
return FromApi(this)->LoadValue(value_id, offset); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
FastAccessorBuilder::ValueId FastAccessorBuilder::LoadObject(ValueId value_id, |
|
||||
int offset) { |
|
||||
return FromApi(this)->LoadObject(value_id, offset); |
|
||||
} |
|
||||
|
|
||||
FastAccessorBuilder::ValueId FastAccessorBuilder::ToSmi(ValueId value_id) { |
|
||||
return FromApi(this)->ToSmi(value_id); |
|
||||
} |
|
||||
|
|
||||
void FastAccessorBuilder::ReturnValue(ValueId value) { |
|
||||
FromApi(this)->ReturnValue(value); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
void FastAccessorBuilder::CheckFlagSetOrReturnNull(ValueId value_id, int mask) { |
|
||||
FromApi(this)->CheckFlagSetOrReturnNull(value_id, mask); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
void FastAccessorBuilder::CheckNotZeroOrReturnNull(ValueId value_id) { |
|
||||
FromApi(this)->CheckNotZeroOrReturnNull(value_id); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
FastAccessorBuilder::LabelId FastAccessorBuilder::MakeLabel() { |
|
||||
return FromApi(this)->MakeLabel(); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
void FastAccessorBuilder::SetLabel(LabelId label_id) { |
|
||||
FromApi(this)->SetLabel(label_id); |
|
||||
} |
|
||||
|
|
||||
void FastAccessorBuilder::Goto(LabelId label_id) { |
|
||||
FromApi(this)->Goto(label_id); |
|
||||
} |
|
||||
|
|
||||
void FastAccessorBuilder::CheckNotZeroOrJump(ValueId value_id, |
|
||||
LabelId label_id) { |
|
||||
FromApi(this)->CheckNotZeroOrJump(value_id, label_id); |
|
||||
} |
|
||||
|
|
||||
FastAccessorBuilder::ValueId FastAccessorBuilder::Call( |
|
||||
v8::FunctionCallback callback, ValueId value_id) { |
|
||||
return FromApi(this)->Call(callback, value_id); |
|
||||
} |
|
||||
|
|
||||
} // namespace experimental
|
|
||||
} // namespace v8
|
|
@ -1,28 +0,0 @@ |
|||||
// Copyright 2015 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_API_EXPERIMENTAL_H_ |
|
||||
#define V8_API_EXPERIMENTAL_H_ |
|
||||
|
|
||||
namespace v8 { |
|
||||
namespace internal { |
|
||||
class Code; |
|
||||
template <typename T> |
|
||||
class MaybeHandle; |
|
||||
} // internal;
|
|
||||
namespace experimental { |
|
||||
class FastAccessorBuilder; |
|
||||
} // experimental
|
|
||||
|
|
||||
namespace internal { |
|
||||
namespace experimental { |
|
||||
|
|
||||
v8::internal::MaybeHandle<v8::internal::Code> BuildCodeFromFastAccessorBuilder( |
|
||||
v8::experimental::FastAccessorBuilder* fast_handler); |
|
||||
|
|
||||
} // namespace experimental
|
|
||||
} // namespace internal
|
|
||||
} // namespace v8
|
|
||||
|
|
||||
#endif // V8_API_EXPERIMENTAL_H_
|
|
File diff suppressed because it is too large
File diff suppressed because it is too large
@ -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 |
File diff suppressed because it is too large
@ -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_
|
@ -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
|
@ -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_
|
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue