From 3288b87e2f75278c7415fbc7c4574bcf7da71295 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 8 Aug 2013 09:20:52 +0200 Subject: Restructure source code Move the v4 engine classes from a subdir of qml/qml into two subdirs (compiler and jsruntime) of the qml module Remove an unsued qv4syntaxchecker class, and move the moth code directly into compiler. Change-Id: I6929bede1f25098e6cb2e68087e779fac16b0c68 Reviewed-by: Simon Hausmann --- src/qml/compiler/compiler.pri | 27 + src/qml/compiler/qv4codegen.cpp | 2605 ++++++++++++++++++++++++++ src/qml/compiler/qv4codegen_p.h | 451 +++++ src/qml/compiler/qv4instr_moth.cpp | 56 + src/qml/compiler/qv4instr_moth_p.h | 621 ++++++ src/qml/compiler/qv4isel_masm.cpp | 1466 +++++++++++++++ src/qml/compiler/qv4isel_masm_p.h | 939 ++++++++++ src/qml/compiler/qv4isel_moth.cpp | 1089 +++++++++++ src/qml/compiler/qv4isel_moth_p.h | 201 ++ src/qml/compiler/qv4isel_p.cpp | 440 +++++ src/qml/compiler/qv4isel_p.h | 165 ++ src/qml/compiler/qv4isel_util_p.h | 87 + src/qml/compiler/qv4jsir.cpp | 1024 ++++++++++ src/qml/compiler/qv4jsir_p.h | 890 +++++++++ src/qml/compiler/qv4ssa.cpp | 2122 +++++++++++++++++++++ src/qml/compiler/qv4ssa_p.h | 127 ++ src/qml/compiler/qv4vme_moth.cpp | 596 ++++++ src/qml/compiler/qv4vme_moth_p.h | 76 + src/qml/jsruntime/jsruntime.pri | 144 ++ src/qml/jsruntime/qv4alloca_p.h | 54 + src/qml/jsruntime/qv4argumentsobject.cpp | 171 ++ src/qml/jsruntime/qv4argumentsobject_p.h | 97 + src/qml/jsruntime/qv4arrayobject.cpp | 865 +++++++++ src/qml/jsruntime/qv4arrayobject_p.h | 100 + src/qml/jsruntime/qv4booleanobject.cpp | 97 + src/qml/jsruntime/qv4booleanobject_p.h | 77 + src/qml/jsruntime/qv4context.cpp | 615 ++++++ src/qml/jsruntime/qv4context_p.h | 227 +++ src/qml/jsruntime/qv4dateobject.cpp | 1316 +++++++++++++ src/qml/jsruntime/qv4dateobject_p.h | 137 ++ src/qml/jsruntime/qv4debugging.cpp | 372 ++++ src/qml/jsruntime/qv4debugging_p.h | 160 ++ src/qml/jsruntime/qv4engine.cpp | 837 +++++++++ src/qml/jsruntime/qv4engine_p.h | 332 ++++ src/qml/jsruntime/qv4errorobject.cpp | 351 ++++ src/qml/jsruntime/qv4errorobject_p.h | 246 +++ src/qml/jsruntime/qv4exception.cpp | 129 ++ src/qml/jsruntime/qv4exception_gcc.cpp | 143 ++ src/qml/jsruntime/qv4exception_p.h | 81 + src/qml/jsruntime/qv4executableallocator.cpp | 220 +++ src/qml/jsruntime/qv4executableallocator_p.h | 134 ++ src/qml/jsruntime/qv4function.cpp | 88 + src/qml/jsruntime/qv4function_p.h | 142 ++ src/qml/jsruntime/qv4functionobject.cpp | 558 ++++++ src/qml/jsruntime/qv4functionobject_p.h | 221 +++ src/qml/jsruntime/qv4global_p.h | 197 ++ src/qml/jsruntime/qv4globalobject.cpp | 652 +++++++ src/qml/jsruntime/qv4globalobject_p.h | 82 + src/qml/jsruntime/qv4identifier.cpp | 172 ++ src/qml/jsruntime/qv4identifier_p.h | 220 +++ src/qml/jsruntime/qv4identifiertable.cpp | 184 ++ src/qml/jsruntime/qv4identifiertable_p.h | 86 + src/qml/jsruntime/qv4include.cpp | 232 +++ src/qml/jsruntime/qv4include_p.h | 113 ++ src/qml/jsruntime/qv4internalclass.cpp | 296 +++ src/qml/jsruntime/qv4internalclass_p.h | 154 ++ src/qml/jsruntime/qv4jsonobject.cpp | 1040 ++++++++++ src/qml/jsruntime/qv4jsonobject_p.h | 85 + src/qml/jsruntime/qv4lookup.cpp | 365 ++++ src/qml/jsruntime/qv4lookup_p.h | 94 + src/qml/jsruntime/qv4managed.cpp | 212 +++ src/qml/jsruntime/qv4managed_p.h | 321 ++++ src/qml/jsruntime/qv4math_p.h | 147 ++ src/qml/jsruntime/qv4mathobject.cpp | 311 +++ src/qml/jsruntime/qv4mathobject_p.h | 78 + src/qml/jsruntime/qv4mm.cpp | 605 ++++++ src/qml/jsruntime/qv4mm_p.h | 157 ++ src/qml/jsruntime/qv4numberobject.cpp | 257 +++ src/qml/jsruntime/qv4numberobject_p.h | 81 + src/qml/jsruntime/qv4object.cpp | 1407 ++++++++++++++ src/qml/jsruntime/qv4object_p.h | 432 +++++ src/qml/jsruntime/qv4objectiterator.cpp | 129 ++ src/qml/jsruntime/qv4objectiterator_p.h | 87 + src/qml/jsruntime/qv4objectproto.cpp | 624 ++++++ src/qml/jsruntime/qv4objectproto_p.h | 107 ++ src/qml/jsruntime/qv4property_p.h | 150 ++ src/qml/jsruntime/qv4qmlextensions.cpp | 51 + src/qml/jsruntime/qv4qmlextensions_p.h | 66 + src/qml/jsruntime/qv4qobjectwrapper.cpp | 1792 ++++++++++++++++++ src/qml/jsruntime/qv4qobjectwrapper_p.h | 201 ++ src/qml/jsruntime/qv4regexp.cpp | 180 ++ src/qml/jsruntime/qv4regexp_p.h | 151 ++ src/qml/jsruntime/qv4regexpobject.cpp | 359 ++++ src/qml/jsruntime/qv4regexpobject_p.h | 123 ++ src/qml/jsruntime/qv4runtime.cpp | 1250 ++++++++++++ src/qml/jsruntime/qv4runtime_p.h | 722 +++++++ src/qml/jsruntime/qv4script.cpp | 249 +++ src/qml/jsruntime/qv4script_p.h | 88 + src/qml/jsruntime/qv4sequenceobject.cpp | 651 +++++++ src/qml/jsruntime/qv4sequenceobject_p.h | 91 + src/qml/jsruntime/qv4serialize.cpp | 399 ++++ src/qml/jsruntime/qv4serialize_p.h | 80 + src/qml/jsruntime/qv4sparsearray.cpp | 459 +++++ src/qml/jsruntime/qv4sparsearray_p.h | 367 ++++ src/qml/jsruntime/qv4stacktrace.cpp | 146 ++ src/qml/jsruntime/qv4stacktrace_p.h | 75 + src/qml/jsruntime/qv4string.cpp | 321 ++++ src/qml/jsruntime/qv4string_p.h | 146 ++ src/qml/jsruntime/qv4stringobject.cpp | 761 ++++++++ src/qml/jsruntime/qv4stringobject_p.h | 110 ++ src/qml/jsruntime/qv4unwindhelper.cpp | 82 + src/qml/jsruntime/qv4unwindhelper_p-arm.h | 233 +++ src/qml/jsruntime/qv4unwindhelper_p-dw2.h | 191 ++ src/qml/jsruntime/qv4unwindhelper_p.h | 72 + src/qml/jsruntime/qv4util_p.h | 74 + src/qml/jsruntime/qv4value.cpp | 403 ++++ src/qml/jsruntime/qv4value_def_p.h | 282 +++ src/qml/jsruntime/qv4value_p.h | 426 +++++ src/qml/jsruntime/qv4variantobject.cpp | 203 ++ src/qml/jsruntime/qv4variantobject_p.h | 102 + src/qml/qml.pro | 2 + src/qml/qml/qml.pri | 1 - src/qml/qml/v4/moth/moth.pri | 13 - src/qml/qml/v4/moth/qv4instr_moth.cpp | 56 - src/qml/qml/v4/moth/qv4instr_moth_p.h | 621 ------ src/qml/qml/v4/moth/qv4isel_moth.cpp | 1089 ----------- src/qml/qml/v4/moth/qv4isel_moth_p.h | 201 -- src/qml/qml/v4/moth/qv4vme_moth.cpp | 596 ------ src/qml/qml/v4/moth/qv4vme_moth_p.h | 76 - src/qml/qml/v4/qv4alloca_p.h | 54 - src/qml/qml/v4/qv4argumentsobject.cpp | 171 -- src/qml/qml/v4/qv4argumentsobject_p.h | 97 - src/qml/qml/v4/qv4arrayobject.cpp | 865 --------- src/qml/qml/v4/qv4arrayobject_p.h | 100 - src/qml/qml/v4/qv4booleanobject.cpp | 97 - src/qml/qml/v4/qv4booleanobject_p.h | 77 - src/qml/qml/v4/qv4codegen.cpp | 2605 -------------------------- src/qml/qml/v4/qv4codegen_p.h | 451 ----- src/qml/qml/v4/qv4context.cpp | 615 ------ src/qml/qml/v4/qv4context_p.h | 227 --- src/qml/qml/v4/qv4dateobject.cpp | 1316 ------------- src/qml/qml/v4/qv4dateobject_p.h | 137 -- src/qml/qml/v4/qv4debugging.cpp | 372 ---- src/qml/qml/v4/qv4debugging_p.h | 161 -- src/qml/qml/v4/qv4engine.cpp | 837 --------- src/qml/qml/v4/qv4engine_p.h | 332 ---- src/qml/qml/v4/qv4errorobject.cpp | 351 ---- src/qml/qml/v4/qv4errorobject_p.h | 246 --- src/qml/qml/v4/qv4exception.cpp | 129 -- src/qml/qml/v4/qv4exception_gcc.cpp | 143 -- src/qml/qml/v4/qv4exception_p.h | 81 - src/qml/qml/v4/qv4executableallocator.cpp | 220 --- src/qml/qml/v4/qv4executableallocator_p.h | 134 -- src/qml/qml/v4/qv4function.cpp | 88 - src/qml/qml/v4/qv4function_p.h | 142 -- src/qml/qml/v4/qv4functionobject.cpp | 558 ------ src/qml/qml/v4/qv4functionobject_p.h | 223 --- src/qml/qml/v4/qv4global_p.h | 197 -- src/qml/qml/v4/qv4globalobject.cpp | 652 ------- src/qml/qml/v4/qv4globalobject_p.h | 82 - src/qml/qml/v4/qv4identifier.cpp | 172 -- src/qml/qml/v4/qv4identifier_p.h | 220 --- src/qml/qml/v4/qv4identifiertable.cpp | 184 -- src/qml/qml/v4/qv4identifiertable_p.h | 86 - src/qml/qml/v4/qv4include.cpp | 232 --- src/qml/qml/v4/qv4include_p.h | 113 -- src/qml/qml/v4/qv4internalclass.cpp | 296 --- src/qml/qml/v4/qv4internalclass_p.h | 154 -- src/qml/qml/v4/qv4isel_masm.cpp | 1466 --------------- src/qml/qml/v4/qv4isel_masm_p.h | 939 ---------- src/qml/qml/v4/qv4isel_p.cpp | 440 ----- src/qml/qml/v4/qv4isel_p.h | 165 -- src/qml/qml/v4/qv4isel_util_p.h | 87 - src/qml/qml/v4/qv4jsir.cpp | 1024 ---------- src/qml/qml/v4/qv4jsir_p.h | 890 --------- src/qml/qml/v4/qv4jsonobject.cpp | 1040 ---------- src/qml/qml/v4/qv4jsonobject_p.h | 85 - src/qml/qml/v4/qv4lookup.cpp | 365 ---- src/qml/qml/v4/qv4lookup_p.h | 94 - src/qml/qml/v4/qv4managed.cpp | 212 --- src/qml/qml/v4/qv4managed_p.h | 321 ---- src/qml/qml/v4/qv4math_p.h | 147 -- src/qml/qml/v4/qv4mathobject.cpp | 311 --- src/qml/qml/v4/qv4mathobject_p.h | 78 - src/qml/qml/v4/qv4mm.cpp | 605 ------ src/qml/qml/v4/qv4mm_p.h | 157 -- src/qml/qml/v4/qv4numberobject.cpp | 257 --- src/qml/qml/v4/qv4numberobject_p.h | 81 - src/qml/qml/v4/qv4object.cpp | 1407 -------------- src/qml/qml/v4/qv4object_p.h | 434 ----- src/qml/qml/v4/qv4objectiterator.cpp | 129 -- src/qml/qml/v4/qv4objectiterator_p.h | 87 - src/qml/qml/v4/qv4objectproto.cpp | 624 ------ src/qml/qml/v4/qv4objectproto_p.h | 107 -- src/qml/qml/v4/qv4property_p.h | 150 -- src/qml/qml/v4/qv4qmlextensions.cpp | 51 - src/qml/qml/v4/qv4qmlextensions_p.h | 66 - src/qml/qml/v4/qv4qobjectwrapper.cpp | 1792 ------------------ src/qml/qml/v4/qv4qobjectwrapper_p.h | 201 -- src/qml/qml/v4/qv4regexp.cpp | 180 -- src/qml/qml/v4/qv4regexp_p.h | 151 -- src/qml/qml/v4/qv4regexpobject.cpp | 359 ---- src/qml/qml/v4/qv4regexpobject_p.h | 123 -- src/qml/qml/v4/qv4runtime.cpp | 1250 ------------ src/qml/qml/v4/qv4runtime_p.h | 722 ------- src/qml/qml/v4/qv4script.cpp | 249 --- src/qml/qml/v4/qv4script_p.h | 88 - src/qml/qml/v4/qv4sequenceobject.cpp | 651 ------- src/qml/qml/v4/qv4sequenceobject_p.h | 91 - src/qml/qml/v4/qv4serialize.cpp | 399 ---- src/qml/qml/v4/qv4serialize_p.h | 80 - src/qml/qml/v4/qv4sparsearray.cpp | 459 ----- src/qml/qml/v4/qv4sparsearray_p.h | 367 ---- src/qml/qml/v4/qv4ssa.cpp | 2122 --------------------- src/qml/qml/v4/qv4ssa_p.h | 127 -- src/qml/qml/v4/qv4stacktrace.cpp | 146 -- src/qml/qml/v4/qv4stacktrace_p.h | 75 - src/qml/qml/v4/qv4string.cpp | 321 ---- src/qml/qml/v4/qv4string_p.h | 146 -- src/qml/qml/v4/qv4stringobject.cpp | 761 -------- src/qml/qml/v4/qv4stringobject_p.h | 110 -- src/qml/qml/v4/qv4syntaxchecker.cpp | 123 -- src/qml/qml/v4/qv4syntaxchecker_p.h | 77 - src/qml/qml/v4/qv4unwindhelper.cpp | 82 - src/qml/qml/v4/qv4unwindhelper_p-arm.h | 233 --- src/qml/qml/v4/qv4unwindhelper_p-dw2.h | 191 -- src/qml/qml/v4/qv4unwindhelper_p.h | 72 - src/qml/qml/v4/qv4util_p.h | 74 - src/qml/qml/v4/qv4value.cpp | 403 ---- src/qml/qml/v4/qv4value_def_p.h | 282 --- src/qml/qml/v4/qv4value_p.h | 426 ----- src/qml/qml/v4/qv4variantobject.cpp | 203 -- src/qml/qml/v4/qv4variantobject_p.h | 102 - src/qml/qml/v4/v4.pri | 161 -- 224 files changed, 40881 insertions(+), 41088 deletions(-) create mode 100644 src/qml/compiler/compiler.pri create mode 100644 src/qml/compiler/qv4codegen.cpp create mode 100644 src/qml/compiler/qv4codegen_p.h create mode 100644 src/qml/compiler/qv4instr_moth.cpp create mode 100644 src/qml/compiler/qv4instr_moth_p.h create mode 100644 src/qml/compiler/qv4isel_masm.cpp create mode 100644 src/qml/compiler/qv4isel_masm_p.h create mode 100644 src/qml/compiler/qv4isel_moth.cpp create mode 100644 src/qml/compiler/qv4isel_moth_p.h create mode 100644 src/qml/compiler/qv4isel_p.cpp create mode 100644 src/qml/compiler/qv4isel_p.h create mode 100644 src/qml/compiler/qv4isel_util_p.h create mode 100644 src/qml/compiler/qv4jsir.cpp create mode 100644 src/qml/compiler/qv4jsir_p.h create mode 100644 src/qml/compiler/qv4ssa.cpp create mode 100644 src/qml/compiler/qv4ssa_p.h create mode 100644 src/qml/compiler/qv4vme_moth.cpp create mode 100644 src/qml/compiler/qv4vme_moth_p.h create mode 100644 src/qml/jsruntime/jsruntime.pri create mode 100644 src/qml/jsruntime/qv4alloca_p.h create mode 100644 src/qml/jsruntime/qv4argumentsobject.cpp create mode 100644 src/qml/jsruntime/qv4argumentsobject_p.h create mode 100644 src/qml/jsruntime/qv4arrayobject.cpp create mode 100644 src/qml/jsruntime/qv4arrayobject_p.h create mode 100644 src/qml/jsruntime/qv4booleanobject.cpp create mode 100644 src/qml/jsruntime/qv4booleanobject_p.h create mode 100644 src/qml/jsruntime/qv4context.cpp create mode 100644 src/qml/jsruntime/qv4context_p.h create mode 100644 src/qml/jsruntime/qv4dateobject.cpp create mode 100644 src/qml/jsruntime/qv4dateobject_p.h create mode 100644 src/qml/jsruntime/qv4debugging.cpp create mode 100644 src/qml/jsruntime/qv4debugging_p.h create mode 100644 src/qml/jsruntime/qv4engine.cpp create mode 100644 src/qml/jsruntime/qv4engine_p.h create mode 100644 src/qml/jsruntime/qv4errorobject.cpp create mode 100644 src/qml/jsruntime/qv4errorobject_p.h create mode 100644 src/qml/jsruntime/qv4exception.cpp create mode 100644 src/qml/jsruntime/qv4exception_gcc.cpp create mode 100644 src/qml/jsruntime/qv4exception_p.h create mode 100644 src/qml/jsruntime/qv4executableallocator.cpp create mode 100644 src/qml/jsruntime/qv4executableallocator_p.h create mode 100644 src/qml/jsruntime/qv4function.cpp create mode 100644 src/qml/jsruntime/qv4function_p.h create mode 100644 src/qml/jsruntime/qv4functionobject.cpp create mode 100644 src/qml/jsruntime/qv4functionobject_p.h create mode 100644 src/qml/jsruntime/qv4global_p.h create mode 100644 src/qml/jsruntime/qv4globalobject.cpp create mode 100644 src/qml/jsruntime/qv4globalobject_p.h create mode 100644 src/qml/jsruntime/qv4identifier.cpp create mode 100644 src/qml/jsruntime/qv4identifier_p.h create mode 100644 src/qml/jsruntime/qv4identifiertable.cpp create mode 100644 src/qml/jsruntime/qv4identifiertable_p.h create mode 100644 src/qml/jsruntime/qv4include.cpp create mode 100644 src/qml/jsruntime/qv4include_p.h create mode 100644 src/qml/jsruntime/qv4internalclass.cpp create mode 100644 src/qml/jsruntime/qv4internalclass_p.h create mode 100644 src/qml/jsruntime/qv4jsonobject.cpp create mode 100644 src/qml/jsruntime/qv4jsonobject_p.h create mode 100644 src/qml/jsruntime/qv4lookup.cpp create mode 100644 src/qml/jsruntime/qv4lookup_p.h create mode 100644 src/qml/jsruntime/qv4managed.cpp create mode 100644 src/qml/jsruntime/qv4managed_p.h create mode 100644 src/qml/jsruntime/qv4math_p.h create mode 100644 src/qml/jsruntime/qv4mathobject.cpp create mode 100644 src/qml/jsruntime/qv4mathobject_p.h create mode 100644 src/qml/jsruntime/qv4mm.cpp create mode 100644 src/qml/jsruntime/qv4mm_p.h create mode 100644 src/qml/jsruntime/qv4numberobject.cpp create mode 100644 src/qml/jsruntime/qv4numberobject_p.h create mode 100644 src/qml/jsruntime/qv4object.cpp create mode 100644 src/qml/jsruntime/qv4object_p.h create mode 100644 src/qml/jsruntime/qv4objectiterator.cpp create mode 100644 src/qml/jsruntime/qv4objectiterator_p.h create mode 100644 src/qml/jsruntime/qv4objectproto.cpp create mode 100644 src/qml/jsruntime/qv4objectproto_p.h create mode 100644 src/qml/jsruntime/qv4property_p.h create mode 100644 src/qml/jsruntime/qv4qmlextensions.cpp create mode 100644 src/qml/jsruntime/qv4qmlextensions_p.h create mode 100644 src/qml/jsruntime/qv4qobjectwrapper.cpp create mode 100644 src/qml/jsruntime/qv4qobjectwrapper_p.h create mode 100644 src/qml/jsruntime/qv4regexp.cpp create mode 100644 src/qml/jsruntime/qv4regexp_p.h create mode 100644 src/qml/jsruntime/qv4regexpobject.cpp create mode 100644 src/qml/jsruntime/qv4regexpobject_p.h create mode 100644 src/qml/jsruntime/qv4runtime.cpp create mode 100644 src/qml/jsruntime/qv4runtime_p.h create mode 100644 src/qml/jsruntime/qv4script.cpp create mode 100644 src/qml/jsruntime/qv4script_p.h create mode 100644 src/qml/jsruntime/qv4sequenceobject.cpp create mode 100644 src/qml/jsruntime/qv4sequenceobject_p.h create mode 100644 src/qml/jsruntime/qv4serialize.cpp create mode 100644 src/qml/jsruntime/qv4serialize_p.h create mode 100644 src/qml/jsruntime/qv4sparsearray.cpp create mode 100644 src/qml/jsruntime/qv4sparsearray_p.h create mode 100644 src/qml/jsruntime/qv4stacktrace.cpp create mode 100644 src/qml/jsruntime/qv4stacktrace_p.h create mode 100644 src/qml/jsruntime/qv4string.cpp create mode 100644 src/qml/jsruntime/qv4string_p.h create mode 100644 src/qml/jsruntime/qv4stringobject.cpp create mode 100644 src/qml/jsruntime/qv4stringobject_p.h create mode 100644 src/qml/jsruntime/qv4unwindhelper.cpp create mode 100644 src/qml/jsruntime/qv4unwindhelper_p-arm.h create mode 100644 src/qml/jsruntime/qv4unwindhelper_p-dw2.h create mode 100644 src/qml/jsruntime/qv4unwindhelper_p.h create mode 100644 src/qml/jsruntime/qv4util_p.h create mode 100644 src/qml/jsruntime/qv4value.cpp create mode 100644 src/qml/jsruntime/qv4value_def_p.h create mode 100644 src/qml/jsruntime/qv4value_p.h create mode 100644 src/qml/jsruntime/qv4variantobject.cpp create mode 100644 src/qml/jsruntime/qv4variantobject_p.h delete mode 100644 src/qml/qml/v4/moth/moth.pri delete mode 100644 src/qml/qml/v4/moth/qv4instr_moth.cpp delete mode 100644 src/qml/qml/v4/moth/qv4instr_moth_p.h delete mode 100644 src/qml/qml/v4/moth/qv4isel_moth.cpp delete mode 100644 src/qml/qml/v4/moth/qv4isel_moth_p.h delete mode 100644 src/qml/qml/v4/moth/qv4vme_moth.cpp delete mode 100644 src/qml/qml/v4/moth/qv4vme_moth_p.h delete mode 100644 src/qml/qml/v4/qv4alloca_p.h delete mode 100644 src/qml/qml/v4/qv4argumentsobject.cpp delete mode 100644 src/qml/qml/v4/qv4argumentsobject_p.h delete mode 100644 src/qml/qml/v4/qv4arrayobject.cpp delete mode 100644 src/qml/qml/v4/qv4arrayobject_p.h delete mode 100644 src/qml/qml/v4/qv4booleanobject.cpp delete mode 100644 src/qml/qml/v4/qv4booleanobject_p.h delete mode 100644 src/qml/qml/v4/qv4codegen.cpp delete mode 100644 src/qml/qml/v4/qv4codegen_p.h delete mode 100644 src/qml/qml/v4/qv4context.cpp delete mode 100644 src/qml/qml/v4/qv4context_p.h delete mode 100644 src/qml/qml/v4/qv4dateobject.cpp delete mode 100644 src/qml/qml/v4/qv4dateobject_p.h delete mode 100644 src/qml/qml/v4/qv4debugging.cpp delete mode 100644 src/qml/qml/v4/qv4debugging_p.h delete mode 100644 src/qml/qml/v4/qv4engine.cpp delete mode 100644 src/qml/qml/v4/qv4engine_p.h delete mode 100644 src/qml/qml/v4/qv4errorobject.cpp delete mode 100644 src/qml/qml/v4/qv4errorobject_p.h delete mode 100644 src/qml/qml/v4/qv4exception.cpp delete mode 100644 src/qml/qml/v4/qv4exception_gcc.cpp delete mode 100644 src/qml/qml/v4/qv4exception_p.h delete mode 100644 src/qml/qml/v4/qv4executableallocator.cpp delete mode 100644 src/qml/qml/v4/qv4executableallocator_p.h delete mode 100644 src/qml/qml/v4/qv4function.cpp delete mode 100644 src/qml/qml/v4/qv4function_p.h delete mode 100644 src/qml/qml/v4/qv4functionobject.cpp delete mode 100644 src/qml/qml/v4/qv4functionobject_p.h delete mode 100644 src/qml/qml/v4/qv4global_p.h delete mode 100644 src/qml/qml/v4/qv4globalobject.cpp delete mode 100644 src/qml/qml/v4/qv4globalobject_p.h delete mode 100644 src/qml/qml/v4/qv4identifier.cpp delete mode 100644 src/qml/qml/v4/qv4identifier_p.h delete mode 100644 src/qml/qml/v4/qv4identifiertable.cpp delete mode 100644 src/qml/qml/v4/qv4identifiertable_p.h delete mode 100644 src/qml/qml/v4/qv4include.cpp delete mode 100644 src/qml/qml/v4/qv4include_p.h delete mode 100644 src/qml/qml/v4/qv4internalclass.cpp delete mode 100644 src/qml/qml/v4/qv4internalclass_p.h delete mode 100644 src/qml/qml/v4/qv4isel_masm.cpp delete mode 100644 src/qml/qml/v4/qv4isel_masm_p.h delete mode 100644 src/qml/qml/v4/qv4isel_p.cpp delete mode 100644 src/qml/qml/v4/qv4isel_p.h delete mode 100644 src/qml/qml/v4/qv4isel_util_p.h delete mode 100644 src/qml/qml/v4/qv4jsir.cpp delete mode 100644 src/qml/qml/v4/qv4jsir_p.h delete mode 100644 src/qml/qml/v4/qv4jsonobject.cpp delete mode 100644 src/qml/qml/v4/qv4jsonobject_p.h delete mode 100644 src/qml/qml/v4/qv4lookup.cpp delete mode 100644 src/qml/qml/v4/qv4lookup_p.h delete mode 100644 src/qml/qml/v4/qv4managed.cpp delete mode 100644 src/qml/qml/v4/qv4managed_p.h delete mode 100644 src/qml/qml/v4/qv4math_p.h delete mode 100644 src/qml/qml/v4/qv4mathobject.cpp delete mode 100644 src/qml/qml/v4/qv4mathobject_p.h delete mode 100644 src/qml/qml/v4/qv4mm.cpp delete mode 100644 src/qml/qml/v4/qv4mm_p.h delete mode 100644 src/qml/qml/v4/qv4numberobject.cpp delete mode 100644 src/qml/qml/v4/qv4numberobject_p.h delete mode 100644 src/qml/qml/v4/qv4object.cpp delete mode 100644 src/qml/qml/v4/qv4object_p.h delete mode 100644 src/qml/qml/v4/qv4objectiterator.cpp delete mode 100644 src/qml/qml/v4/qv4objectiterator_p.h delete mode 100644 src/qml/qml/v4/qv4objectproto.cpp delete mode 100644 src/qml/qml/v4/qv4objectproto_p.h delete mode 100644 src/qml/qml/v4/qv4property_p.h delete mode 100644 src/qml/qml/v4/qv4qmlextensions.cpp delete mode 100644 src/qml/qml/v4/qv4qmlextensions_p.h delete mode 100644 src/qml/qml/v4/qv4qobjectwrapper.cpp delete mode 100644 src/qml/qml/v4/qv4qobjectwrapper_p.h delete mode 100644 src/qml/qml/v4/qv4regexp.cpp delete mode 100644 src/qml/qml/v4/qv4regexp_p.h delete mode 100644 src/qml/qml/v4/qv4regexpobject.cpp delete mode 100644 src/qml/qml/v4/qv4regexpobject_p.h delete mode 100644 src/qml/qml/v4/qv4runtime.cpp delete mode 100644 src/qml/qml/v4/qv4runtime_p.h delete mode 100644 src/qml/qml/v4/qv4script.cpp delete mode 100644 src/qml/qml/v4/qv4script_p.h delete mode 100644 src/qml/qml/v4/qv4sequenceobject.cpp delete mode 100644 src/qml/qml/v4/qv4sequenceobject_p.h delete mode 100644 src/qml/qml/v4/qv4serialize.cpp delete mode 100644 src/qml/qml/v4/qv4serialize_p.h delete mode 100644 src/qml/qml/v4/qv4sparsearray.cpp delete mode 100644 src/qml/qml/v4/qv4sparsearray_p.h delete mode 100644 src/qml/qml/v4/qv4ssa.cpp delete mode 100644 src/qml/qml/v4/qv4ssa_p.h delete mode 100644 src/qml/qml/v4/qv4stacktrace.cpp delete mode 100644 src/qml/qml/v4/qv4stacktrace_p.h delete mode 100644 src/qml/qml/v4/qv4string.cpp delete mode 100644 src/qml/qml/v4/qv4string_p.h delete mode 100644 src/qml/qml/v4/qv4stringobject.cpp delete mode 100644 src/qml/qml/v4/qv4stringobject_p.h delete mode 100644 src/qml/qml/v4/qv4syntaxchecker.cpp delete mode 100644 src/qml/qml/v4/qv4syntaxchecker_p.h delete mode 100644 src/qml/qml/v4/qv4unwindhelper.cpp delete mode 100644 src/qml/qml/v4/qv4unwindhelper_p-arm.h delete mode 100644 src/qml/qml/v4/qv4unwindhelper_p-dw2.h delete mode 100644 src/qml/qml/v4/qv4unwindhelper_p.h delete mode 100644 src/qml/qml/v4/qv4util_p.h delete mode 100644 src/qml/qml/v4/qv4value.cpp delete mode 100644 src/qml/qml/v4/qv4value_def_p.h delete mode 100644 src/qml/qml/v4/qv4value_p.h delete mode 100644 src/qml/qml/v4/qv4variantobject.cpp delete mode 100644 src/qml/qml/v4/qv4variantobject_p.h delete mode 100644 src/qml/qml/v4/v4.pri (limited to 'src') diff --git a/src/qml/compiler/compiler.pri b/src/qml/compiler/compiler.pri new file mode 100644 index 0000000000..61578fd011 --- /dev/null +++ b/src/qml/compiler/compiler.pri @@ -0,0 +1,27 @@ +include(../../3rdparty/masm/masm-defs.pri) + +INCLUDEPATH += $$PWD +INCLUDEPATH += $$OUT_PWD + +HEADERS += \ + $$PWD/qv4codegen_p.h \ + $$PWD/qv4isel_masm_p.h \ + $$PWD/qv4isel_p.h \ + $$PWD/qv4jsir_p.h \ + $$PWD/qv4vme_moth_p.h \ + $$PWD/qv4instr_moth_p.h \ + $$PWD/qv4isel_moth_p.h \ + $$PWD/qv4isel_util_p.h \ + $$PWD/qv4ssa_p.h + +SOURCES += \ + $$PWD/qv4codegen.cpp \ + $$PWD/qv4instr_moth.cpp \ + $$PWD/qv4isel_masm.cpp \ + $$PWD/qv4isel_moth.cpp \ + $$PWD/qv4isel_p.cpp \ + $$PWD/qv4jsir.cpp \ + $$PWD/qv4ssa.cpp \ + $$PWD/qv4vme_moth.cpp + +include(../../3rdparty/masm/masm.pri) diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp new file mode 100644 index 0000000000..d0c43c8f56 --- /dev/null +++ b/src/qml/compiler/qv4codegen.cpp @@ -0,0 +1,2605 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4codegen_p.h" +#include "qv4util_p.h" +#include "qv4debugging_p.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONST +#undef CONST +#endif + +#define QV4_NO_LIVENESS +#undef SHOW_SSA + +using namespace QQmlJS; +using namespace AST; + +class Codegen::ScanFunctions: Visitor +{ + typedef QV4::TemporaryAssignment TemporaryBoolAssignment; +public: + ScanFunctions(Codegen *cg, const QString &sourceCode) + : _cg(cg) + , _sourceCode(sourceCode) + , _env(0) + , _inFuncBody(false) + , _allowFuncDecls(true) + { + } + + void operator()(Node *node) + { + if (node) + node->accept(this); + } + + inline void enterEnvironment(Node *node) + { + Environment *e = _cg->newEnvironment(node, _env); + if (!e->isStrict) + e->isStrict = _cg->_strictMode; + _envStack.append(e); + _env = e; + } + + inline void leaveEnvironment() + { + _envStack.pop(); + _env = _envStack.isEmpty() ? 0 : _envStack.top(); + } + +protected: + using Visitor::visit; + using Visitor::endVisit; + + void checkDirectivePrologue(SourceElements *ast) + { + for (SourceElements *it = ast; it; it = it->next) { + if (StatementSourceElement *stmt = cast(it->element)) { + if (ExpressionStatement *expr = cast(stmt->statement)) { + if (StringLiteral *strLit = cast(expr->expression)) { + // Use the source code, because the StringLiteral's + // value might have escape sequences in it, which is not + // allowed. + if (strLit->literalToken.length < 2) + continue; + QStringRef str = _sourceCode.midRef(strLit->literalToken.offset + 1, strLit->literalToken.length - 2); + if (str == QStringLiteral("use strict")) { + _env->isStrict = true; + } else { + // TODO: give a warning. + } + continue; + } + } + } + + break; + } + } + + void checkName(const QStringRef &name, const SourceLocation &loc) + { + if (_env->isStrict) { + if (name == QLatin1String("implements") + || name == QLatin1String("interface") + || name == QLatin1String("let") + || name == QLatin1String("package") + || name == QLatin1String("private") + || name == QLatin1String("protected") + || name == QLatin1String("public") + || name == QLatin1String("static") + || name == QLatin1String("yield")) { + _cg->throwSyntaxError(loc, QCoreApplication::translate("qv4codegen", "Unexpected strict mode reserved word")); + } + } + } + void checkForArguments(AST::FormalParameterList *parameters) + { + while (parameters) { + if (parameters->name == QStringLiteral("arguments")) + _env->usesArgumentsObject = Environment::ArgumentsObjectNotUsed; + parameters = parameters->next; + } + } + + virtual bool visit(Program *ast) + { + enterEnvironment(ast); + checkDirectivePrologue(ast->elements); + return true; + } + + virtual void endVisit(Program *) + { + leaveEnvironment(); + } + + virtual bool visit(CallExpression *ast) + { + if (! _env->hasDirectEval) { + if (IdentifierExpression *id = cast(ast->base)) { + if (id->name == QStringLiteral("eval")) { + if (_env->usesArgumentsObject == Environment::ArgumentsObjectUnknown) + _env->usesArgumentsObject = Environment::ArgumentsObjectUsed; + _env->hasDirectEval = true; + } + } + } + int argc = 0; + for (ArgumentList *it = ast->arguments; it; it = it->next) + ++argc; + _env->maxNumberOfArguments = qMax(_env->maxNumberOfArguments, argc); + return true; + } + + virtual bool visit(NewMemberExpression *ast) + { + int argc = 0; + for (ArgumentList *it = ast->arguments; it; it = it->next) + ++argc; + _env->maxNumberOfArguments = qMax(_env->maxNumberOfArguments, argc); + return true; + } + + virtual bool visit(ArrayLiteral *ast) + { + int index = 0; + for (ElementList *it = ast->elements; it; it = it->next) { + for (Elision *elision = it->elision; elision; elision = elision->next) + ++index; + ++index; + } + if (ast->elision) { + for (Elision *elision = ast->elision->next; elision; elision = elision->next) + ++index; + } + _env->maxNumberOfArguments = qMax(_env->maxNumberOfArguments, index); + return true; + } + + virtual bool visit(VariableDeclaration *ast) + { + if (_env->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments"))) + _cg->throwSyntaxError(ast->identifierToken, QCoreApplication::translate("qv4codegen", "Variable name may not be eval or arguments in strict mode")); + checkName(ast->name, ast->identifierToken); + if (ast->name == QLatin1String("arguments")) + _env->usesArgumentsObject = Environment::ArgumentsObjectNotUsed; + _env->enter(ast->name.toString(), ast->expression ? Environment::VariableDefinition : Environment::VariableDeclaration); + return true; + } + + virtual bool visit(IdentifierExpression *ast) + { + checkName(ast->name, ast->identifierToken); + if (_env->usesArgumentsObject == Environment::ArgumentsObjectUnknown && ast->name == QLatin1String("arguments")) + _env->usesArgumentsObject = Environment::ArgumentsObjectUsed; + return true; + } + + virtual bool visit(ExpressionStatement *ast) + { + if (FunctionExpression* expr = AST::cast(ast->expression)) { + if (!_allowFuncDecls) + _cg->throwSyntaxError(expr->functionToken, QCoreApplication::translate("qv4codegen", "conditional function or closure declaration")); + + enterFunction(expr, /*enterName*/ true); + Node::accept(expr->formals, this); + Node::accept(expr->body, this); + leaveEnvironment(); + return false; + } else { + SourceLocation firstToken = ast->firstSourceLocation(); + if (_sourceCode.midRef(firstToken.offset, firstToken.length) == QStringLiteral("function")) { + _cg->throwSyntaxError(firstToken, QCoreApplication::translate("qv4codegen", "unexpected token")); + } + } + return true; + } + + virtual bool visit(FunctionExpression *ast) + { + enterFunction(ast, /*enterName*/ false); + return true; + } + + void enterFunction(FunctionExpression *ast, bool enterName, bool isExpression = true) + { + if (_env->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments"))) + _cg->throwSyntaxError(ast->identifierToken, QCoreApplication::translate("qv4codegen", "Function name may not be eval or arguments in strict mode")); + enterFunction(ast, ast->name.toString(), ast->formals, ast->body, enterName ? ast : 0, isExpression); + } + + virtual void endVisit(FunctionExpression *) + { + leaveEnvironment(); + } + + virtual bool visit(ObjectLiteral *ast) + { + int argc = 0; + for (PropertyAssignmentList *it = ast->properties; it; it = it->next) { + ++argc; + if (AST::cast(it->assignment)) + ++argc; + } + _env->maxNumberOfArguments = qMax(_env->maxNumberOfArguments, argc); + + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, true); + Node::accept(ast->properties, this); + return false; + } + + virtual bool visit(PropertyGetterSetter *ast) + { + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, true); + enterFunction(ast, QString(), ast->formals, ast->functionBody, /*FunctionExpression*/0, /*isExpression*/false); + return true; + } + + virtual void endVisit(PropertyGetterSetter *) + { + leaveEnvironment(); + } + + virtual bool visit(FunctionDeclaration *ast) + { + enterFunction(ast, /*enterName*/ true, /*isExpression */false); + return true; + } + + virtual void endVisit(FunctionDeclaration *) + { + leaveEnvironment(); + } + + virtual bool visit(FunctionBody *ast) + { + TemporaryBoolAssignment inFuncBody(_inFuncBody, true); + Node::accept(ast->elements, this); + return false; + } + + virtual bool visit(WithStatement *ast) + { + if (_env->isStrict) { + _cg->throwSyntaxError(ast->withToken, QCoreApplication::translate("qv4codegen", "'with' statement is not allowed in strict mode")); + return false; + } + + return true; + } + + virtual bool visit(IfStatement *ast) { + Node::accept(ast->expression, this); + + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_inFuncBody); + Node::accept(ast->ok, this); + Node::accept(ast->ko, this); + + return false; + } + + virtual bool visit(WhileStatement *ast) { + Node::accept(ast->expression, this); + + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_inFuncBody); + Node::accept(ast->statement, this); + + return false; + } + + virtual bool visit(DoWhileStatement *ast) { + { + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_env->isStrict); + Node::accept(ast->statement, this); + } + Node::accept(ast->expression, this); + return false; + } + + virtual bool visit(ForStatement *ast) { + Node::accept(ast->initialiser, this); + Node::accept(ast->condition, this); + Node::accept(ast->expression, this); + + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_env->isStrict); + Node::accept(ast->statement, this); + + return false; + } + + virtual bool visit(LocalForStatement *ast) { + Node::accept(ast->declarations, this); + Node::accept(ast->condition, this); + Node::accept(ast->expression, this); + + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_env->isStrict); + Node::accept(ast->statement, this); + + return false; + } + + virtual bool visit(ForEachStatement *ast) { + Node::accept(ast->initialiser, this); + Node::accept(ast->expression, this); + + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_env->isStrict); + Node::accept(ast->statement, this); + + return false; + } + + virtual bool visit(LocalForEachStatement *ast) { + Node::accept(ast->declaration, this); + Node::accept(ast->expression, this); + + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_env->isStrict); + Node::accept(ast->statement, this); + + return false; + } + + virtual bool visit(Block *ast) { + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, _env->isStrict ? false : _allowFuncDecls); + Node::accept(ast->statements, this); + return false; + } + +private: + void enterFunction(Node *ast, const QString &name, FormalParameterList *formals, FunctionBody *body, FunctionExpression *expr, bool isExpression) + { + bool wasStrict = false; + if (_env) { + _env->hasNestedFunctions = true; + // The identifier of a function expression cannot be referenced from the enclosing environment. + if (expr) + _env->enter(name, Environment::FunctionDefinition, expr); + if (name == QLatin1String("arguments")) + _env->usesArgumentsObject = Environment::ArgumentsObjectNotUsed; + wasStrict = _env->isStrict; + } + + enterEnvironment(ast); + checkForArguments(formals); + + _env->isNamedFunctionExpression = isExpression && !name.isEmpty(); + _env->formals = formals; + + if (body) + checkDirectivePrologue(body->elements); + + if (wasStrict || _env->isStrict) { + QStringList args; + for (FormalParameterList *it = formals; it; it = it->next) { + QString arg = it->name.toString(); + if (args.contains(arg)) + _cg->throwSyntaxError(it->identifierToken, QCoreApplication::translate("qv4codegen", "Duplicate parameter name '%1' is not allowed in strict mode").arg(arg)); + if (arg == QLatin1String("eval") || arg == QLatin1String("arguments")) + _cg->throwSyntaxError(it->identifierToken, QCoreApplication::translate("qv4codegen", "'%1' cannot be used as parameter name in strict mode").arg(arg)); + args += arg; + } + } + } + +private: // fields: + Codegen *_cg; + const QString _sourceCode; + Environment *_env; + QStack _envStack; + + bool _inFuncBody; + bool _allowFuncDecls; +}; + +Codegen::Codegen(QV4::ExecutionContext *context, bool strict) + : _module(0) + , _function(0) + , _block(0) + , _exitBlock(0) + , _throwBlock(0) + , _returnAddress(0) + , _mode(GlobalCode) + , _env(0) + , _loop(0) + , _labelledStatement(0) + , _scopeAndFinally(0) + , _context(context) + , _strictMode(strict) + , _errorHandler(0) +{ +} + +Codegen::Codegen(ErrorHandler *errorHandler, bool strictMode) + : _module(0) + , _function(0) + , _block(0) + , _exitBlock(0) + , _throwBlock(0) + , _returnAddress(0) + , _mode(GlobalCode) + , _env(0) + , _loop(0) + , _labelledStatement(0) + , _scopeAndFinally(0) + , _context(0) + , _strictMode(strictMode) + , _errorHandler(errorHandler) +{ +} + +V4IR::Function *Codegen::operator()(const QString &fileName, + const QString &sourceCode, + Program *node, + V4IR::Module *module, + Mode mode, + const QStringList &inheritedLocals) +{ + assert(node); + + _fileName = fileName; + _module = module; + _env = 0; + + ScanFunctions scan(this, sourceCode); + scan(node); + + V4IR::Function *globalCode = defineFunction(QStringLiteral("%entry"), node, 0, + node->elements, mode, inheritedLocals); + qDeleteAll(_envMap); + _envMap.clear(); + + return globalCode; +} + +V4IR::Function *Codegen::operator()(const QString &fileName, + const QString &sourceCode, + AST::FunctionExpression *ast, + V4IR::Module *module) +{ + _fileName = fileName; + _module = module; + _env = 0; + + ScanFunctions scan(this, sourceCode); + // fake a global environment + scan.enterEnvironment(0); + scan(ast); + scan.leaveEnvironment(); + + V4IR::Function *function = defineFunction(ast->name.toString(), ast, ast->formals, ast->body ? ast->body->elements : 0); + + qDeleteAll(_envMap); + _envMap.clear(); + + return function; +} + + +void Codegen::enterEnvironment(Node *node) +{ + _env = _envMap.value(node); + assert(_env); +} + +void Codegen::leaveEnvironment() +{ + assert(_env); + _env = _env->parent; +} + +void Codegen::enterLoop(Statement *node, V4IR::BasicBlock *startBlock, V4IR::BasicBlock *breakBlock, V4IR::BasicBlock *continueBlock) +{ + if (startBlock) + startBlock->markAsGroupStart(); + _loop = new Loop(node, startBlock, breakBlock, continueBlock, _loop); + _loop->labelledStatement = _labelledStatement; // consume the enclosing labelled statement + _loop->scopeAndFinally = _scopeAndFinally; + _labelledStatement = 0; +} + +void Codegen::leaveLoop() +{ + Loop *current = _loop; + _loop = _loop->parent; + delete current; +} + +V4IR::Expr *Codegen::member(V4IR::Expr *base, const QString *name) +{ + if (base->asTemp() /*|| base->asName()*/) + return _block->MEMBER(base->asTemp(), name); + else { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), base); + return _block->MEMBER(_block->TEMP(t), name); + } +} + +V4IR::Expr *Codegen::subscript(V4IR::Expr *base, V4IR::Expr *index) +{ + if (! base->asTemp()) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), base); + base = _block->TEMP(t); + } + + if (! index->asTemp()) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), index); + index = _block->TEMP(t); + } + + assert(base->asTemp() && index->asTemp()); + return _block->SUBSCRIPT(base->asTemp(), index->asTemp()); +} + +V4IR::Expr *Codegen::argument(V4IR::Expr *expr) +{ + if (expr && ! expr->asTemp()) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), expr); + expr = _block->TEMP(t); + } + return expr; +} + +// keeps references alive, converts other expressions to temps +V4IR::Expr *Codegen::reference(V4IR::Expr *expr) +{ + if (expr && !expr->asTemp() && !expr->asName() && !expr->asMember() && !expr->asSubscript()) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), expr); + expr = _block->TEMP(t); + } + return expr; +} + +V4IR::Expr *Codegen::unop(V4IR::AluOp op, V4IR::Expr *expr) +{ + Q_ASSERT(op != V4IR::OpIncrement); + Q_ASSERT(op != V4IR::OpDecrement); + + if (V4IR::Const *c = expr->asConst()) { + if (c->type == V4IR::NumberType) { + switch (op) { + case V4IR::OpNot: + return _block->CONST(V4IR::BoolType, !c->value); + case V4IR::OpUMinus: + return _block->CONST(V4IR::NumberType, -c->value); + case V4IR::OpUPlus: + return expr; + case V4IR::OpCompl: + return _block->CONST(V4IR::NumberType, ~QV4::Value::toInt32(c->value)); + case V4IR::OpIncrement: + return _block->CONST(V4IR::NumberType, c->value + 1); + case V4IR::OpDecrement: + return _block->CONST(V4IR::NumberType, c->value - 1); + default: + break; + } + } + } + if (! expr->asTemp()) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), expr); + expr = _block->TEMP(t); + } + assert(expr->asTemp()); + return _block->UNOP(op, expr->asTemp()); +} + +V4IR::Expr *Codegen::binop(V4IR::AluOp op, V4IR::Expr *left, V4IR::Expr *right) +{ + if (V4IR::Const *c1 = left->asConst()) { + if (V4IR::Const *c2 = right->asConst()) { + if (c1->type == V4IR::NumberType && c2->type == V4IR::NumberType) { + switch (op) { + case V4IR::OpAdd: return _block->CONST(V4IR::NumberType, c1->value + c2->value); + case V4IR::OpAnd: return _block->CONST(V4IR::BoolType, c1->value ? c2->value : 0); + case V4IR::OpBitAnd: return _block->CONST(V4IR::NumberType, int(c1->value) & int(c2->value)); + case V4IR::OpBitOr: return _block->CONST(V4IR::NumberType, int(c1->value) | int(c2->value)); + case V4IR::OpBitXor: return _block->CONST(V4IR::NumberType, int(c1->value) ^ int(c2->value)); + case V4IR::OpDiv: return _block->CONST(V4IR::NumberType, c1->value / c2->value); + case V4IR::OpEqual: return _block->CONST(V4IR::BoolType, c1->value == c2->value); + case V4IR::OpNotEqual: return _block->CONST(V4IR::BoolType, c1->value != c2->value); + case V4IR::OpStrictEqual: return _block->CONST(V4IR::BoolType, c1->value == c2->value); + case V4IR::OpStrictNotEqual: return _block->CONST(V4IR::BoolType, c1->value != c2->value); + case V4IR::OpGe: return _block->CONST(V4IR::BoolType, c1->value >= c2->value); + case V4IR::OpGt: return _block->CONST(V4IR::BoolType, c1->value > c2->value); + case V4IR::OpLe: return _block->CONST(V4IR::BoolType, c1->value <= c2->value); + case V4IR::OpLt: return _block->CONST(V4IR::BoolType, c1->value < c2->value); + case V4IR::OpLShift: return _block->CONST(V4IR::NumberType, QV4::Value::toInt32(c1->value) << (QV4::Value::toUInt32(c2->value) & 0x1f)); + case V4IR::OpMod: return _block->CONST(V4IR::NumberType, std::fmod(c1->value, c2->value)); + case V4IR::OpMul: return _block->CONST(V4IR::NumberType, c1->value * c2->value); + case V4IR::OpOr: return _block->CONST(V4IR::NumberType, c1->value ? c1->value : c2->value); + case V4IR::OpRShift: return _block->CONST(V4IR::NumberType, QV4::Value::toInt32(c1->value) >> (QV4::Value::toUInt32(c2->value) & 0x1f)); + case V4IR::OpSub: return _block->CONST(V4IR::NumberType, c1->value - c2->value); + case V4IR::OpURShift: return _block->CONST(V4IR::NumberType,QV4::Value::toUInt32(c1->value) >> (QV4::Value::toUInt32(c2->value) & 0x1f)); + + case V4IR::OpInstanceof: + case V4IR::OpIn: + break; + + case V4IR::OpIfTrue: // unary ops + case V4IR::OpNot: + case V4IR::OpUMinus: + case V4IR::OpUPlus: + case V4IR::OpCompl: + case V4IR::OpIncrement: + case V4IR::OpDecrement: + case V4IR::OpInvalid: + break; + } + } + } + } else if (op == V4IR::OpAdd) { + if (V4IR::String *s1 = left->asString()) { + if (V4IR::String *s2 = right->asString()) { + return _block->STRING(_function->newString(*s1->value + *s2->value)); + } + } + } + + if (!left->asTemp()) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), left); + left = _block->TEMP(t); + } + + if (!right->asTemp()) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), right); + right = _block->TEMP(t); + } + + assert(left->asTemp()); + assert(right->asTemp()); + + return _block->BINOP(op, left, right); +} + +V4IR::Expr *Codegen::call(V4IR::Expr *base, V4IR::ExprList *args) +{ + base = reference(base); + return _block->CALL(base, args); +} + +void Codegen::move(V4IR::Expr *target, V4IR::Expr *source, V4IR::AluOp op) +{ + assert(target->isLValue()); + + // TODO: verify the rest of the function for when op == OpInvalid + if (op != V4IR::OpInvalid) { + move(target, binop(op, target, source), V4IR::OpInvalid); + return; + } + + if (!source->asTemp() && !source->asConst() && (op != V4IR::OpInvalid || ! target->asTemp())) { + unsigned t = _block->newTemp(); + _block->MOVE(_block->TEMP(t), source); + source = _block->TEMP(t); + } + if (source->asConst() && (!target->asTemp() || op != V4IR::OpInvalid)) { + unsigned t = _block->newTemp(); + _block->MOVE(_block->TEMP(t), source); + source = _block->TEMP(t); + } + + _block->MOVE(target, source, op); +} + +void Codegen::cjump(V4IR::Expr *cond, V4IR::BasicBlock *iftrue, V4IR::BasicBlock *iffalse) +{ + if (! (cond->asTemp() || cond->asBinop())) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), cond); + cond = _block->TEMP(t); + } + _block->CJUMP(cond, iftrue, iffalse); +} + +void Codegen::accept(Node *node) +{ + if (node) + node->accept(this); +} + +void Codegen::statement(Statement *ast) +{ + _block->nextLocation = ast->firstSourceLocation(); + accept(ast); +} + +void Codegen::statement(ExpressionNode *ast) +{ + if (! ast) { + return; + } else { + Result r(nx); + qSwap(_expr, r); + accept(ast); + qSwap(_expr, r); + if (r.format == ex) { + if (r->asCall()) { + _block->EXP(*r); // the nest nx representation for calls is EXP(CALL(c..)) + } else if (r->asTemp()) { + // there is nothing to do + } else { + unsigned t = _block->newTemp(); + move(_block->TEMP(t), *r); + } + } + } +} + +void Codegen::condition(ExpressionNode *ast, V4IR::BasicBlock *iftrue, V4IR::BasicBlock *iffalse) +{ + if (ast) { + Result r(iftrue, iffalse); + qSwap(_expr, r); + accept(ast); + qSwap(_expr, r); + if (r.format == ex) { + cjump(*r, r.iftrue, r.iffalse); + } + } +} + +Codegen::Result Codegen::expression(ExpressionNode *ast) +{ + Result r; + if (ast) { + qSwap(_expr, r); + accept(ast); + qSwap(_expr, r); + } + return r; +} + +QString Codegen::propertyName(PropertyName *ast) +{ + QString p; + if (ast) { + qSwap(_property, p); + accept(ast); + qSwap(_property, p); + } + return p; +} + +Codegen::Result Codegen::sourceElement(SourceElement *ast) +{ + Result r(nx); + if (ast) { + qSwap(_expr, r); + accept(ast); + qSwap(_expr, r); + } + return r; +} + +Codegen::UiMember Codegen::uiObjectMember(UiObjectMember *ast) +{ + UiMember m; + if (ast) { + qSwap(_uiMember, m); + accept(ast); + qSwap(_uiMember, m); + } + return m; +} + +void Codegen::functionBody(FunctionBody *ast) +{ + if (ast) + sourceElements(ast->elements); +} + +void Codegen::program(Program *ast) +{ + if (ast) { + sourceElements(ast->elements); + } +} + +void Codegen::sourceElements(SourceElements *ast) +{ + for (SourceElements *it = ast; it; it = it->next) { + sourceElement(it->element); + } +} + +void Codegen::variableDeclaration(VariableDeclaration *ast) +{ + V4IR::Expr *initializer = 0; + if (!ast->expression) + return; + Result expr = expression(ast->expression); + assert(expr.code); + initializer = *expr; + + if (! _env->parent || _function->insideWithOrCatch) { + // it's global code. + move(_block->NAME(ast->name.toString(), ast->identifierToken.startLine, ast->identifierToken.startColumn), initializer); + } else { + const int index = _env->findMember(ast->name.toString()); + assert(index != -1); + move(_block->LOCAL(index, 0), initializer); + } +} + +void Codegen::variableDeclarationList(VariableDeclarationList *ast) +{ + for (VariableDeclarationList *it = ast; it; it = it->next) { + variableDeclaration(it->declaration); + } +} + + +bool Codegen::visit(ArgumentList *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(CaseBlock *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(CaseClause *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(CaseClauses *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(Catch *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(DefaultClause *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(ElementList *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(Elision *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(Finally *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(FormalParameterList *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(FunctionBody *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(Program *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(PropertyAssignmentList *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(PropertyNameAndValue *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(PropertyGetterSetter *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(SourceElements *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(StatementList *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(UiArrayMemberList *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(UiImport *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(UiImportList *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(UiObjectInitializer *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(UiObjectMemberList *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(UiParameterList *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(UiProgram *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(UiQualifiedId *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(VariableDeclaration *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(VariableDeclarationList *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(Expression *ast) +{ + statement(ast->left); + accept(ast->right); + return false; +} + +bool Codegen::visit(ArrayLiteral *ast) +{ + V4IR::ExprList *args = 0; + V4IR::ExprList *current = 0; + for (ElementList *it = ast->elements; it; it = it->next) { + for (Elision *elision = it->elision; elision; elision = elision->next) { + V4IR::ExprList *arg = _function->New(); + if (!current) { + args = arg; + } else { + current->next = arg; + } + current = arg; + current->expr = _block->CONST(V4IR::MissingType, 0); + } + Result expr = expression(it->expression); + + V4IR::ExprList *arg = _function->New(); + if (!current) { + args = arg; + } else { + current->next = arg; + } + current = arg; + + V4IR::Expr *exp = *expr; + if (exp->asTemp() || exp->asConst()) { + current->expr = exp; + } else { + unsigned value = _block->newTemp(); + move(_block->TEMP(value), exp); + current->expr = _block->TEMP(value); + } + } + for (Elision *elision = ast->elision; elision; elision = elision->next) { + V4IR::ExprList *arg = _function->New(); + if (!current) { + args = arg; + } else { + current->next = arg; + } + current = arg; + current->expr = _block->CONST(V4IR::MissingType, 0); + } + + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), _block->CALL(_block->NAME(V4IR::Name::builtin_define_array, 0, 0), args)); + _expr.code = _block->TEMP(t); + return false; +} + +bool Codegen::visit(ArrayMemberExpression *ast) +{ + Result base = expression(ast->base); + Result index = expression(ast->expression); + _expr.code = subscript(*base, *index); + return false; +} + +static V4IR::AluOp baseOp(int op) +{ + switch ((QSOperator::Op) op) { + case QSOperator::InplaceAnd: return V4IR::OpBitAnd; + case QSOperator::InplaceSub: return V4IR::OpSub; + case QSOperator::InplaceDiv: return V4IR::OpDiv; + case QSOperator::InplaceAdd: return V4IR::OpAdd; + case QSOperator::InplaceLeftShift: return V4IR::OpLShift; + case QSOperator::InplaceMod: return V4IR::OpMod; + case QSOperator::InplaceMul: return V4IR::OpMul; + case QSOperator::InplaceOr: return V4IR::OpBitOr; + case QSOperator::InplaceRightShift: return V4IR::OpRShift; + case QSOperator::InplaceURightShift: return V4IR::OpURShift; + case QSOperator::InplaceXor: return V4IR::OpBitXor; + default: return V4IR::OpInvalid; + } +} + +bool Codegen::visit(BinaryExpression *ast) +{ + if (ast->op == QSOperator::And) { + if (_expr.accept(cx)) { + V4IR::BasicBlock *iftrue = _function->newBasicBlock(groupStartBlock()); + condition(ast->left, iftrue, _expr.iffalse); + _block = iftrue; + condition(ast->right, _expr.iftrue, _expr.iffalse); + } else { + V4IR::BasicBlock *iftrue = _function->newBasicBlock(groupStartBlock()); + V4IR::BasicBlock *endif = _function->newBasicBlock(groupStartBlock()); + + const unsigned r = _block->newTemp(); + + move(_block->TEMP(r), *expression(ast->left)); + cjump(_block->TEMP(r), iftrue, endif); + _block = iftrue; + move(_block->TEMP(r), *expression(ast->right)); + _block->JUMP(endif); + + _expr.code = _block->TEMP(r); + _block = endif; + } + return false; + } else if (ast->op == QSOperator::Or) { + if (_expr.accept(cx)) { + V4IR::BasicBlock *iffalse = _function->newBasicBlock(groupStartBlock()); + condition(ast->left, _expr.iftrue, iffalse); + _block = iffalse; + condition(ast->right, _expr.iftrue, _expr.iffalse); + } else { + V4IR::BasicBlock *iffalse = _function->newBasicBlock(groupStartBlock()); + V4IR::BasicBlock *endif = _function->newBasicBlock(groupStartBlock()); + + const unsigned r = _block->newTemp(); + move(_block->TEMP(r), *expression(ast->left)); + cjump(_block->TEMP(r), endif, iffalse); + _block = iffalse; + move(_block->TEMP(r), *expression(ast->right)); + _block->JUMP(endif); + + _block = endif; + _expr.code = _block->TEMP(r); + } + return false; + } + + V4IR::Expr* left = *expression(ast->left); + throwSyntaxErrorOnEvalOrArgumentsInStrictMode(left, ast->left->lastSourceLocation()); + + switch (ast->op) { + case QSOperator::Or: + case QSOperator::And: + break; + + case QSOperator::Assign: { + V4IR::Expr* right = *expression(ast->right); + if (! (left->asTemp() || left->asName() || left->asSubscript() || left->asMember())) + throwReferenceError(ast->operatorToken, QCoreApplication::translate("qv4codegen", "left-hand side of assignment operator is not an lvalue")); + + if (_expr.accept(nx)) { + move(left, right); + } else { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), right); + move(left, _block->TEMP(t)); + _expr.code = _block->TEMP(t); + } + break; + } + + case QSOperator::InplaceAnd: + case QSOperator::InplaceSub: + case QSOperator::InplaceDiv: + case QSOperator::InplaceAdd: + case QSOperator::InplaceLeftShift: + case QSOperator::InplaceMod: + case QSOperator::InplaceMul: + case QSOperator::InplaceOr: + case QSOperator::InplaceRightShift: + case QSOperator::InplaceURightShift: + case QSOperator::InplaceXor: { + V4IR::Expr* right = *expression(ast->right); + if (!left->isLValue()) + throwSyntaxError(ast->operatorToken, QCoreApplication::translate("qv4codegen", "left-hand side of inplace operator is not an lvalue")); + + if (_expr.accept(nx)) { + move(left, right, baseOp(ast->op)); + } else { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), right); + move(left, _block->TEMP(t), baseOp(ast->op)); + _expr.code = left; + } + break; + } + + case QSOperator::In: + case QSOperator::InstanceOf: + case QSOperator::Equal: + case QSOperator::NotEqual: + case QSOperator::Ge: + case QSOperator::Gt: + case QSOperator::Le: + case QSOperator::Lt: + case QSOperator::StrictEqual: + case QSOperator::StrictNotEqual: { + if (!left->asTemp() && !left->asConst()) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), left); + left = _block->TEMP(t); + } + + V4IR::Expr* right = *expression(ast->right); + + if (_expr.accept(cx)) { + cjump(binop(V4IR::binaryOperator(ast->op), left, right), _expr.iftrue, _expr.iffalse); + } else { + V4IR::Expr *e = binop(V4IR::binaryOperator(ast->op), left, right); + if (e->asConst() || e->asString()) + _expr.code = e; + else { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), e); + _expr.code = _block->TEMP(t); + } + } + break; + } + + case QSOperator::Add: + case QSOperator::BitAnd: + case QSOperator::BitOr: + case QSOperator::BitXor: + case QSOperator::Div: + case QSOperator::LShift: + case QSOperator::Mod: + case QSOperator::Mul: + case QSOperator::RShift: + case QSOperator::Sub: + case QSOperator::URShift: { + if (!left->asTemp() && !left->asConst()) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), left); + left = _block->TEMP(t); + } + + V4IR::Expr* right = *expression(ast->right); + + V4IR::Expr *e = binop(V4IR::binaryOperator(ast->op), left, right); + if (e->asConst() || e->asString()) + _expr.code = e; + else { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), e); + _expr.code = _block->TEMP(t); + } + break; + } + + } // switch + + return false; +} + +bool Codegen::visit(CallExpression *ast) +{ + Result base = expression(ast->base); + V4IR::ExprList *args = 0, **args_it = &args; + for (ArgumentList *it = ast->arguments; it; it = it->next) { + Result arg = expression(it->expression); + V4IR::Expr *actual = argument(*arg); + *args_it = _function->New(); + (*args_it)->init(actual); + args_it = &(*args_it)->next; + } + _expr.code = call(*base, args); + return false; +} + +bool Codegen::visit(ConditionalExpression *ast) +{ + V4IR::BasicBlock *iftrue = _function->newBasicBlock(groupStartBlock()); + V4IR::BasicBlock *iffalse = _function->newBasicBlock(groupStartBlock()); + V4IR::BasicBlock *endif = _function->newBasicBlock(groupStartBlock()); + + const unsigned t = _block->newTemp(); + + condition(ast->expression, iftrue, iffalse); + + _block = iftrue; + move(_block->TEMP(t), *expression(ast->ok)); + _block->JUMP(endif); + + _block = iffalse; + move(_block->TEMP(t), *expression(ast->ko)); + _block->JUMP(endif); + + _block = endif; + + _expr.code = _block->TEMP(t); + + return false; +} + +bool Codegen::visit(DeleteExpression *ast) +{ + V4IR::Expr* expr = *expression(ast->expression); + // Temporaries cannot be deleted + if (expr->asTemp() && expr->asTemp()->index < _env->members.size()) { + // Trying to delete a function argument might throw. + if (_function->isStrict && expr->asTemp()->index < 0) + throwSyntaxError(ast->deleteToken, "Delete of an unqualified identifier in strict mode."); + _expr.code = _block->CONST(V4IR::BoolType, 0); + return false; + } + if (_function->isStrict && expr->asName()) + throwSyntaxError(ast->deleteToken, "Delete of an unqualified identifier in strict mode."); + + // [[11.4.1]] Return true if it's not a reference + if (expr->asConst() || expr->asString()) { + _expr.code = _block->CONST(V4IR::BoolType, 1); + return false; + } + + // Return values from calls are also not a reference, but we have to + // perform the call to allow for side effects. + if (expr->asCall()) { + _block->EXP(expr); + _expr.code = _block->CONST(V4IR::BoolType, 1); + return false; + } + if (expr->asTemp() && expr->asTemp()->index >= _env->members.size()) { + _expr.code = _block->CONST(V4IR::BoolType, 1); + return false; + } + + V4IR::ExprList *args = _function->New(); + args->init(reference(expr)); + _expr.code = call(_block->NAME(V4IR::Name::builtin_delete, ast->deleteToken.startLine, ast->deleteToken.startColumn), args); + return false; +} + +bool Codegen::visit(FalseLiteral *) +{ + if (_expr.accept(cx)) { + _block->JUMP(_expr.iffalse); + } else { + _expr.code = _block->CONST(V4IR::BoolType, 0); + } + return false; +} + +bool Codegen::visit(FieldMemberExpression *ast) +{ + Result base = expression(ast->base); + _expr.code = member(*base, _function->newString(ast->name.toString())); + return false; +} + +bool Codegen::visit(FunctionExpression *ast) +{ + V4IR::Function *function = defineFunction(ast->name.toString(), ast, ast->formals, ast->body ? ast->body->elements : 0); + _expr.code = _block->CLOSURE(function); + return false; +} + +V4IR::Expr *Codegen::identifier(const QString &name, int line, int col) +{ + uint scope = 0; + Environment *e = _env; + V4IR::Function *f = _function; + + while (f && e->parent) { + if ((f->usesArgumentsObject && name == "arguments") || (!f->isStrict && f->hasDirectEval) || f->insideWithOrCatch || (f->isNamedExpression && f->name == name)) + break; + int index = e->findMember(name); + assert (index < e->members.size()); + if (index != -1) { + return _block->LOCAL(index, scope); + } + const int argIdx = f->indexOfArgument(&name); + if (argIdx != -1) + return _block->ARG(argIdx, scope); + ++scope; + e = e->parent; + f = f->outer; + } + + if (!e->parent && (!f || !f->insideWithOrCatch) && _mode != EvalCode && _mode != QmlBinding && (!f || f->name != name)) + return _block->GLOBALNAME(name, line, col); + + // global context or with. Lookup by name + return _block->NAME(name, line, col); + +} + +bool Codegen::visit(IdentifierExpression *ast) +{ + _expr.code = identifier(ast->name.toString(), ast->identifierToken.startLine, ast->identifierToken.startColumn); + return false; +} + +bool Codegen::visit(NestedExpression *ast) +{ + accept(ast->expression); + return false; +} + +bool Codegen::visit(NewExpression *ast) +{ + Result base = expression(ast->expression); + V4IR::Expr *expr = *base; + if (expr && !expr->asTemp() && !expr->asName() && !expr->asMember()) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), expr); + expr = _block->TEMP(t); + } + _expr.code = _block->NEW(expr, 0); + return false; +} + +bool Codegen::visit(NewMemberExpression *ast) +{ + Result base = expression(ast->base); + V4IR::Expr *expr = *base; + if (expr && !expr->asTemp() && !expr->asName() && !expr->asMember()) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), expr); + expr = _block->TEMP(t); + } + + V4IR::ExprList *args = 0, **args_it = &args; + for (ArgumentList *it = ast->arguments; it; it = it->next) { + Result arg = expression(it->expression); + V4IR::Expr *actual = argument(*arg); + *args_it = _function->New(); + (*args_it)->init(actual); + args_it = &(*args_it)->next; + } + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), _block->NEW(expr, args)); + _expr.code = _block->TEMP(t); + return false; +} + +bool Codegen::visit(NotExpression *ast) +{ + Result expr = expression(ast->expression); + const unsigned r = _block->newTemp(); + move(_block->TEMP(r), unop(V4IR::OpNot, *expr)); + _expr.code = _block->TEMP(r); + return false; +} + +bool Codegen::visit(NullExpression *) +{ + if (_expr.accept(cx)) _block->JUMP(_expr.iffalse); + else _expr.code = _block->CONST(V4IR::NullType, 0); + + return false; +} + +bool Codegen::visit(NumericLiteral *ast) +{ + if (_expr.accept(cx)) { + if (ast->value) _block->JUMP(_expr.iftrue); + else _block->JUMP(_expr.iffalse); + } else { + _expr.code = _block->CONST(V4IR::NumberType, ast->value); + } + return false; +} + +struct ObjectPropertyValue { + V4IR::Expr *value; + V4IR::Function *getter; + V4IR::Function *setter; +}; + +bool Codegen::visit(ObjectLiteral *ast) +{ + QMap valueMap; + + for (PropertyAssignmentList *it = ast->properties; it; it = it->next) { + if (PropertyNameAndValue *nv = AST::cast(it->assignment)) { + QString name = propertyName(nv->name); + Result value = expression(nv->value); + ObjectPropertyValue &v = valueMap[name]; + if (v.getter || v.setter || (_function->isStrict && v.value)) + throwSyntaxError(nv->lastSourceLocation(), + QCoreApplication::translate("qv4codegen", "Illegal duplicate key '%1' in object literal").arg(name)); + + valueMap[name].value = *value; + } else if (PropertyGetterSetter *gs = AST::cast(it->assignment)) { + QString name = propertyName(gs->name); + V4IR::Function *function = defineFunction(name, gs, gs->formals, gs->functionBody ? gs->functionBody->elements : 0); + ObjectPropertyValue &v = valueMap[name]; + if (v.value || + (gs->type == PropertyGetterSetter::Getter && v.getter) || + (gs->type == PropertyGetterSetter::Setter && v.setter)) + throwSyntaxError(gs->lastSourceLocation(), + QCoreApplication::translate("qv4codegen", "Illegal duplicate key '%1' in object literal").arg(name)); + if (gs->type == PropertyGetterSetter::Getter) + v.getter = function; + else + v.setter = function; + } else { + Q_UNREACHABLE(); + } + } + + V4IR::ExprList *args = 0; + + if (!valueMap.isEmpty()) { + V4IR::ExprList *current; + for (QMap::iterator it = valueMap.begin(); it != valueMap.end(); ) { + if (QV4::String(0, it.key()).asArrayIndex() != UINT_MAX) { + ++it; + continue; + } + + if (!args) { + args = _function->New(); + current = args; + } else { + current->next = _function->New(); + current = current->next; + } + + current->expr = _block->NAME(it.key(), 0, 0); + + if (it->value) { + current->next = _function->New(); + current = current->next; + current->expr = _block->CONST(V4IR::BoolType, true); + + unsigned value = _block->newTemp(); + move(_block->TEMP(value), it->value); + + current->next = _function->New(); + current = current->next; + current->expr = _block->TEMP(value); + } else { + current->next = _function->New(); + current = current->next; + current->expr = _block->CONST(V4IR::BoolType, false); + + unsigned getter = _block->newTemp(); + unsigned setter = _block->newTemp(); + move(_block->TEMP(getter), it->getter ? _block->CLOSURE(it->getter) : _block->CONST(V4IR::UndefinedType, 0)); + move(_block->TEMP(setter), it->setter ? _block->CLOSURE(it->setter) : _block->CONST(V4IR::UndefinedType, 0)); + + current->next = _function->New(); + current = current->next; + current->expr = _block->TEMP(getter); + current->next = _function->New(); + current = current->next; + current->expr = _block->TEMP(setter); + } + + it = valueMap.erase(it); + } + } + + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), _block->CALL(_block->NAME(V4IR::Name::builtin_define_object_literal, + ast->firstSourceLocation().startLine, ast->firstSourceLocation().startColumn), args)); + + // What's left are array entries + if (!valueMap.isEmpty()) { + unsigned value = 0; + unsigned getter = 0; + unsigned setter = 0; + for (QMap::const_iterator it = valueMap.constBegin(); it != valueMap.constEnd(); ++it) { + V4IR::ExprList *args = _function->New(); + V4IR::ExprList *current = args; + current->expr = _block->TEMP(t); + current->next = _function->New(); + current = current->next; + current->expr = _block->NAME(it.key(), 0, 0); + current->next = _function->New(); + current = current->next; + + if (it->value) { + if (!value) + value = _block->newTemp(); + move(_block->TEMP(value), it->value); + // __qmljs_builtin_define_property(Value object, String *name, Value val, ExecutionContext *ctx) + current->expr = _block->TEMP(value); + _block->EXP(_block->CALL(_block->NAME(V4IR::Name::builtin_define_property, 0, 0), args)); + } else { + if (!getter) { + getter = _block->newTemp(); + setter = _block->newTemp(); + } + move(_block->TEMP(getter), it->getter ? _block->CLOSURE(it->getter) : _block->CONST(V4IR::UndefinedType, 0)); + move(_block->TEMP(setter), it->setter ? _block->CLOSURE(it->setter) : _block->CONST(V4IR::UndefinedType, 0)); + + + // __qmljs_builtin_define_getter_setter(Value object, String *name, Value getter, Value setter, ExecutionContext *ctx); + current->expr = _block->TEMP(getter); + current->next = _function->New(); + current = current->next; + current->expr = _block->TEMP(setter); + _block->EXP(_block->CALL(_block->NAME(V4IR::Name::builtin_define_getter_setter, 0, 0), args)); + } + } + } + + _expr.code = _block->TEMP(t); + return false; +} + +bool Codegen::visit(PostDecrementExpression *ast) +{ + Result expr = expression(ast->base); + if (!expr->isLValue()) + throwReferenceError(ast->base->lastSourceLocation(), "Invalid left-hand side expression in postfix operation"); + throwSyntaxErrorOnEvalOrArgumentsInStrictMode(*expr, ast->decrementToken); + + if (_expr.accept(nx)) { + move(*expr, binop(V4IR::OpSub, *expr, _block->CONST(V4IR::NumberType, 1))); + } else { + V4IR::ExprList *args = _function->New(); + args->init(*expr); + _expr.code = call(_block->NAME(V4IR::Name::builtin_postdecrement, ast->lastSourceLocation().startLine, ast->lastSourceLocation().startColumn), args); + } + return false; +} + +bool Codegen::visit(PostIncrementExpression *ast) +{ + Result expr = expression(ast->base); + if (!expr->isLValue()) + throwReferenceError(ast->base->lastSourceLocation(), "Invalid left-hand side expression in postfix operation"); + throwSyntaxErrorOnEvalOrArgumentsInStrictMode(*expr, ast->incrementToken); + + if (_expr.accept(nx)) { + move(*expr, binop(V4IR::OpAdd, unop(V4IR::OpUPlus, *expr), _block->CONST(V4IR::NumberType, 1))); + } else { + V4IR::ExprList *args = _function->New(); + args->init(*expr); + _expr.code = call(_block->NAME(V4IR::Name::builtin_postincrement, ast->lastSourceLocation().startLine, ast->lastSourceLocation().startColumn), args); + } + return false; +} + +bool Codegen::visit(PreDecrementExpression *ast) +{ + Result expr = expression(ast->expression); + throwSyntaxErrorOnEvalOrArgumentsInStrictMode(*expr, ast->decrementToken); + V4IR::Expr *op = binop(V4IR::OpSub, *expr, _block->CONST(V4IR::NumberType, 1)); + if (_expr.accept(nx)) { + move(*expr, op); + } else { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), op); + move(*expr, _block->TEMP(t)); + _expr.code = _block->TEMP(t); + } + return false; +} + +bool Codegen::visit(PreIncrementExpression *ast) +{ + Result expr = expression(ast->expression); + throwSyntaxErrorOnEvalOrArgumentsInStrictMode(*expr, ast->incrementToken); + V4IR::Expr *op = binop(V4IR::OpAdd, unop(V4IR::OpUPlus, *expr), _block->CONST(V4IR::NumberType, 1)); + if (_expr.accept(nx)) { + move(*expr, op); + } else { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), op); + move(*expr, _block->TEMP(t)); + _expr.code = _block->TEMP(t); + } + return false; +} + +bool Codegen::visit(RegExpLiteral *ast) +{ + _expr.code = _block->REGEXP(_function->newString(ast->pattern.toString()), ast->flags); + return false; +} + +bool Codegen::visit(StringLiteral *ast) +{ + _expr.code = _block->STRING(_function->newString(ast->value.toString())); + return false; +} + +bool Codegen::visit(ThisExpression *ast) +{ + _expr.code = _block->NAME(QStringLiteral("this"), ast->thisToken.startLine, ast->thisToken.startColumn); + return false; +} + +bool Codegen::visit(TildeExpression *ast) +{ + Result expr = expression(ast->expression); + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), unop(V4IR::OpCompl, *expr)); + _expr.code = _block->TEMP(t); + return false; +} + +bool Codegen::visit(TrueLiteral *) +{ + if (_expr.accept(cx)) { + _block->JUMP(_expr.iftrue); + } else { + _expr.code = _block->CONST(V4IR::BoolType, 1); + } + return false; +} + +bool Codegen::visit(TypeOfExpression *ast) +{ + Result expr = expression(ast->expression); + V4IR::ExprList *args = _function->New(); + args->init(reference(*expr)); + _expr.code = call(_block->NAME(V4IR::Name::builtin_typeof, ast->typeofToken.startLine, ast->typeofToken.startColumn), args); + return false; +} + +bool Codegen::visit(UnaryMinusExpression *ast) +{ + Result expr = expression(ast->expression); + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), unop(V4IR::OpUMinus, *expr)); + _expr.code = _block->TEMP(t); + return false; +} + +bool Codegen::visit(UnaryPlusExpression *ast) +{ + Result expr = expression(ast->expression); + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), unop(V4IR::OpUPlus, *expr)); + _expr.code = _block->TEMP(t); + return false; +} + +bool Codegen::visit(VoidExpression *ast) +{ + statement(ast->expression); + _expr.code = _block->CONST(V4IR::UndefinedType, 0); + return false; +} + +bool Codegen::visit(FunctionDeclaration * ast) +{ + if (_mode == QmlBinding) + move(_block->TEMP(_returnAddress), _block->NAME(ast->name.toString(), 0, 0)); + _expr.accept(nx); + return false; +} + +V4IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast, + AST::FormalParameterList *formals, + AST::SourceElements *body, Mode mode, + const QStringList &inheritedLocals) +{ + qSwap(_mode, mode); // enter function code. + Loop *loop = 0; + qSwap(_loop, loop); + + ScopeAndFinally *scopeAndFinally = 0; + + enterEnvironment(ast); + V4IR::Function *function = _module->newFunction(name, _function); + function->sourceFile = _fileName; + + V4IR::BasicBlock *entryBlock = function->newBasicBlock(groupStartBlock()); + V4IR::BasicBlock *exitBlock = function->newBasicBlock(groupStartBlock(), V4IR::Function::DontInsertBlock); + V4IR::BasicBlock *throwBlock = function->newBasicBlock(groupStartBlock()); + function->hasDirectEval = _env->hasDirectEval; + function->usesArgumentsObject = (_env->usesArgumentsObject == Environment::ArgumentsObjectUsed); + function->maxNumberOfArguments = _env->maxNumberOfArguments; + function->isStrict = _env->isStrict; + function->isNamedExpression = _env->isNamedFunctionExpression; + + // variables in global code are properties of the global context object, not locals as with other functions. + if (_mode == FunctionCode) { + unsigned t = 0; + for (Environment::MemberMap::iterator it = _env->members.begin(); it != _env->members.end(); ++it) { + const QString &local = it.key(); + function->LOCAL(local); + (*it).index = t; + entryBlock->MOVE(entryBlock->LOCAL(t, 0), entryBlock->CONST(V4IR::UndefinedType, 0)); + ++t; + } + } else { + if (!_env->isStrict) { + foreach (const QString &inheritedLocal, inheritedLocals) { + function->LOCAL(inheritedLocal); + unsigned tempIndex = entryBlock->newTemp(); + Environment::Member member = { Environment::UndefinedMember, + static_cast(tempIndex), 0 }; + _env->members.insert(inheritedLocal, member); + } + } + + V4IR::ExprList *args = 0; + for (Environment::MemberMap::const_iterator it = _env->members.constBegin(); it != _env->members.constEnd(); ++it) { + const QString &local = it.key(); + V4IR::ExprList *next = function->New(); + next->expr = entryBlock->NAME(local, 0, 0); + next->next = args; + args = next; + } + if (args) { + V4IR::ExprList *next = function->New(); + next->expr = entryBlock->CONST(V4IR::BoolType, (mode == EvalCode || mode == QmlBinding)); + next->next = args; + args = next; + + entryBlock->EXP(entryBlock->CALL(entryBlock->NAME(V4IR::Name::builtin_declare_vars, 0, 0), args)); + } + } + + unsigned returnAddress = entryBlock->newTemp(); + + entryBlock->MOVE(entryBlock->TEMP(returnAddress), entryBlock->CONST(V4IR::UndefinedType, 0)); + exitBlock->RET(exitBlock->TEMP(returnAddress)); + V4IR::ExprList *throwArgs = function->New(); + throwArgs->expr = throwBlock->TEMP(returnAddress); + throwBlock->EXP(throwBlock->CALL(throwBlock->NAME(V4IR::Name::builtin_throw, /*line*/0, /*column*/0), throwArgs)); + throwBlock->JUMP(exitBlock); + + qSwap(_function, function); + qSwap(_block, entryBlock); + qSwap(_exitBlock, exitBlock); + qSwap(_throwBlock, throwBlock); + qSwap(_returnAddress, returnAddress); + qSwap(_scopeAndFinally, scopeAndFinally); + + for (FormalParameterList *it = formals; it; it = it->next) { + _function->RECEIVE(it->name.toString()); + } + + foreach (const Environment::Member &member, _env->members) { + if (member.function) { + V4IR::Function *function = defineFunction(member.function->name.toString(), member.function, member.function->formals, + member.function->body ? member.function->body->elements : 0); + if (! _env->parent) { + move(_block->NAME(member.function->name.toString(), member.function->identifierToken.startLine, member.function->identifierToken.startColumn), + _block->CLOSURE(function)); + } else { + assert(member.index >= 0); + move(_block->LOCAL(member.index, 0), _block->CLOSURE(function)); + } + } + } + + sourceElements(body); + + _function->insertBasicBlock(_exitBlock); + + _block->JUMP(_exitBlock); + + qSwap(_function, function); + qSwap(_block, entryBlock); + qSwap(_exitBlock, exitBlock); + qSwap(_throwBlock, throwBlock); + qSwap(_returnAddress, returnAddress); + qSwap(_scopeAndFinally, scopeAndFinally); + qSwap(_loop, loop); + + leaveEnvironment(); + + qSwap(_mode, mode); + + return function; +} + +bool Codegen::visit(IdentifierPropertyName *ast) +{ + _property = ast->id.toString(); + return false; +} + +bool Codegen::visit(NumericLiteralPropertyName *ast) +{ + _property = QString::number(ast->id, 'g', 16); + return false; +} + +bool Codegen::visit(StringLiteralPropertyName *ast) +{ + _property = ast->id.toString(); + return false; +} + +bool Codegen::visit(FunctionSourceElement *ast) +{ + statement(ast->declaration); + return false; +} + +bool Codegen::visit(StatementSourceElement *ast) +{ + statement(ast->statement); + return false; +} + +bool Codegen::visit(Block *ast) +{ + for (StatementList *it = ast->statements; it; it = it->next) { + statement(it->statement); + } + return false; +} + +bool Codegen::visit(BreakStatement *ast) +{ + if (!_loop) + throwSyntaxError(ast->lastSourceLocation(), QCoreApplication::translate("qv4codegen", "Break outside of loop")); + Loop *loop = 0; + if (ast->label.isEmpty()) + loop = _loop; + else { + for (loop = _loop; loop; loop = loop->parent) { + if (loop->labelledStatement && loop->labelledStatement->label == ast->label) + break; + } + if (!loop) + throwSyntaxError(ast->lastSourceLocation(), QCoreApplication::translate("qv4codegen", "Undefined label '%1'").arg(ast->label.toString())); + } + unwindException(loop->scopeAndFinally); + _block->JUMP(loop->breakBlock); + return false; +} + +bool Codegen::visit(ContinueStatement *ast) +{ + Loop *loop = 0; + if (ast->label.isEmpty()) { + for (loop = _loop; loop; loop = loop->parent) { + if (loop->continueBlock) + break; + } + } else { + for (loop = _loop; loop; loop = loop->parent) { + if (loop->labelledStatement && loop->labelledStatement->label == ast->label) { + if (!loop->continueBlock) + loop = 0; + break; + } + } + if (!loop) + throwSyntaxError(ast->lastSourceLocation(), QCoreApplication::translate("qv4codegen", "Undefined label '%1'").arg(ast->label.toString())); + } + if (!loop) + throwSyntaxError(ast->lastSourceLocation(), QCoreApplication::translate("qv4codegen", "continue outside of loop")); + unwindException(loop->scopeAndFinally); + _block->JUMP(loop->continueBlock); + return false; +} + +bool Codegen::visit(DebuggerStatement *) +{ + Q_UNIMPLEMENTED(); + return false; +} + +bool Codegen::visit(DoWhileStatement *ast) +{ + V4IR::BasicBlock *loopbody = _function->newBasicBlock(groupStartBlock()); + V4IR::BasicBlock *loopcond = _function->newBasicBlock(loopbody); + V4IR::BasicBlock *loopend = _function->newBasicBlock(groupStartBlock()); + + enterLoop(ast, loopbody, loopend, loopcond); + + _block->JUMP(loopbody); + + _block = loopbody; + statement(ast->statement); + _block->JUMP(loopcond); + + _block = loopcond; + condition(ast->expression, loopbody, loopend); + + _block = loopend; + + leaveLoop(); + + return false; +} + +bool Codegen::visit(EmptyStatement *) +{ + return false; +} + +bool Codegen::visit(ExpressionStatement *ast) +{ + if (_mode == EvalCode || _mode == QmlBinding) { + Result e = expression(ast->expression); + if (*e) + move(_block->TEMP(_returnAddress), *e); + } else { + statement(ast->expression); + } + return false; +} + +bool Codegen::visit(ForEachStatement *ast) +{ + V4IR::BasicBlock *foreachin = _function->newBasicBlock(groupStartBlock()); + V4IR::BasicBlock *foreachbody = _function->newBasicBlock(foreachin); + V4IR::BasicBlock *foreachend = _function->newBasicBlock(groupStartBlock()); + + enterLoop(ast, foreachin, foreachend, foreachin); + + int objectToIterateOn = _block->newTemp(); + move(_block->TEMP(objectToIterateOn), *expression(ast->expression)); + V4IR::ExprList *args = _function->New(); + args->init(_block->TEMP(objectToIterateOn)); + + int iterator = _block->newTemp(); + move(_block->TEMP(iterator), _block->CALL(_block->NAME(V4IR::Name::builtin_foreach_iterator_object, 0, 0), args)); + + _block->JUMP(foreachin); + + _block = foreachbody; + int temp = _block->newTemp(); + move(*expression(ast->initialiser), _block->TEMP(temp)); + statement(ast->statement); + _block->JUMP(foreachin); + + _block = foreachin; + + args = _function->New(); + args->init(_block->TEMP(iterator)); + move(_block->TEMP(temp), _block->CALL(_block->NAME(V4IR::Name::builtin_foreach_next_property_name, 0, 0), args)); + int null = _block->newTemp(); + move(_block->TEMP(null), _block->CONST(V4IR::NullType, 0)); + cjump(_block->BINOP(V4IR::OpStrictNotEqual, _block->TEMP(temp), _block->TEMP(null)), foreachbody, foreachend); + _block = foreachend; + + leaveLoop(); + return false; +} + +bool Codegen::visit(ForStatement *ast) +{ + V4IR::BasicBlock *forcond = _function->newBasicBlock(groupStartBlock()); + V4IR::BasicBlock *forbody = _function->newBasicBlock(forcond); + V4IR::BasicBlock *forstep = _function->newBasicBlock(forcond); + V4IR::BasicBlock *forend = _function->newBasicBlock(groupStartBlock()); + + enterLoop(ast, forcond, forend, forstep); + + statement(ast->initialiser); + _block->JUMP(forcond); + + _block = forcond; + if (ast->condition) + condition(ast->condition, forbody, forend); + else + _block->JUMP(forbody); + + _block = forbody; + statement(ast->statement); + _block->JUMP(forstep); + + _block = forstep; + statement(ast->expression); + _block->JUMP(forcond); + + _block = forend; + + leaveLoop(); + + return false; +} + +bool Codegen::visit(IfStatement *ast) +{ + V4IR::BasicBlock *iftrue = _function->newBasicBlock(groupStartBlock()); + V4IR::BasicBlock *iffalse = ast->ko ? _function->newBasicBlock(groupStartBlock()) : 0; + V4IR::BasicBlock *endif = _function->newBasicBlock(groupStartBlock()); + condition(ast->expression, iftrue, ast->ko ? iffalse : endif); + + _block = iftrue; + statement(ast->ok); + _block->JUMP(endif); + + if (ast->ko) { + _block = iffalse; + statement(ast->ko); + _block->JUMP(endif); + } + + _block = endif; + + return false; +} + +bool Codegen::visit(LabelledStatement *ast) +{ + // check that no outer loop contains the label + Loop *l = _loop; + while (l) { + if (l->labelledStatement->label == ast->label) { + QString error = QString(QStringLiteral("Label '%1' has already been declared")).arg(ast->label.toString()); + throwSyntaxError(ast->firstSourceLocation(), error); + } + l = l->parent; + } + _labelledStatement = ast; + + if (AST::cast(ast->statement) || + AST::cast(ast->statement) || + AST::cast(ast->statement) || + AST::cast(ast->statement) || + AST::cast(ast->statement) || + AST::cast(ast->statement) || + AST::cast(ast->statement)) { + statement(ast->statement); // labelledStatement will be associated with the ast->statement's loop. + } else { + V4IR::BasicBlock *breakBlock = _function->newBasicBlock(groupStartBlock()); + enterLoop(ast->statement, 0, breakBlock, /*continueBlock*/ 0); + statement(ast->statement); + _block->JUMP(breakBlock); + _block = breakBlock; + leaveLoop(); + } + + return false; +} + +bool Codegen::visit(LocalForEachStatement *ast) +{ + V4IR::BasicBlock *foreachin = _function->newBasicBlock(groupStartBlock()); + V4IR::BasicBlock *foreachbody = _function->newBasicBlock(foreachin); + V4IR::BasicBlock *foreachend = _function->newBasicBlock(groupStartBlock()); + + enterLoop(ast, foreachin, foreachend, foreachin); + + variableDeclaration(ast->declaration); + + int iterator = _block->newTemp(); + move(_block->TEMP(iterator), *expression(ast->expression)); + V4IR::ExprList *args = _function->New(); + args->init(_block->TEMP(iterator)); + move(_block->TEMP(iterator), _block->CALL(_block->NAME(V4IR::Name::builtin_foreach_iterator_object, 0, 0), args)); + + _block->JUMP(foreachin); + + _block = foreachbody; + int temp = _block->newTemp(); + move(identifier(ast->declaration->name.toString()), _block->TEMP(temp)); + statement(ast->statement); + _block->JUMP(foreachin); + + _block = foreachin; + + args = _function->New(); + args->init(_block->TEMP(iterator)); + move(_block->TEMP(temp), _block->CALL(_block->NAME(V4IR::Name::builtin_foreach_next_property_name, 0, 0), args)); + int null = _block->newTemp(); + move(_block->TEMP(null), _block->CONST(V4IR::NullType, 0)); + cjump(_block->BINOP(V4IR::OpStrictNotEqual, _block->TEMP(temp), _block->TEMP(null)), foreachbody, foreachend); + _block = foreachend; + + leaveLoop(); + return false; +} + +bool Codegen::visit(LocalForStatement *ast) +{ + V4IR::BasicBlock *forcond = _function->newBasicBlock(groupStartBlock()); + V4IR::BasicBlock *forbody = _function->newBasicBlock(forcond); + V4IR::BasicBlock *forstep = _function->newBasicBlock(forcond); + V4IR::BasicBlock *forend = _function->newBasicBlock(groupStartBlock()); + + enterLoop(ast, forcond, forend, forstep); + + variableDeclarationList(ast->declarations); + _block->JUMP(forcond); + + _block = forcond; + if (ast->condition) + condition(ast->condition, forbody, forend); + else + _block->JUMP(forbody); + + _block = forbody; + statement(ast->statement); + _block->JUMP(forstep); + + _block = forstep; + statement(ast->expression); + _block->JUMP(forcond); + + _block = forend; + + leaveLoop(); + + return false; +} + +bool Codegen::visit(ReturnStatement *ast) +{ + if (_mode != FunctionCode && _mode != QmlBinding) + throwSyntaxError(ast->returnToken, QCoreApplication::translate("qv4codegen", "Return statement outside of function")); + if (ast->expression) { + Result expr = expression(ast->expression); + move(_block->TEMP(_returnAddress), *expr); + } + unwindException(0); + + _block->JUMP(_exitBlock); + return false; +} + +bool Codegen::visit(SwitchStatement *ast) +{ + V4IR::BasicBlock *switchend = _function->newBasicBlock(groupStartBlock()); + + if (ast->block) { + Result lhs = expression(ast->expression); + V4IR::BasicBlock *switchcond = _function->newBasicBlock(groupStartBlock()); + _block->JUMP(switchcond); + V4IR::BasicBlock *previousBlock = 0; + + QHash blockMap; + + enterLoop(ast, 0, switchend, 0); + + for (CaseClauses *it = ast->block->clauses; it; it = it->next) { + CaseClause *clause = it->clause; + + _block = _function->newBasicBlock(groupStartBlock()); + blockMap[clause] = _block; + + if (previousBlock && !previousBlock->isTerminated()) + previousBlock->JUMP(_block); + + for (StatementList *it2 = clause->statements; it2; it2 = it2->next) + statement(it2->statement); + + previousBlock = _block; + } + + if (ast->block->defaultClause) { + _block = _function->newBasicBlock(groupStartBlock()); + blockMap[ast->block->defaultClause] = _block; + + if (previousBlock && !previousBlock->isTerminated()) + previousBlock->JUMP(_block); + + for (StatementList *it2 = ast->block->defaultClause->statements; it2; it2 = it2->next) + statement(it2->statement); + + previousBlock = _block; + } + + for (CaseClauses *it = ast->block->moreClauses; it; it = it->next) { + CaseClause *clause = it->clause; + + _block = _function->newBasicBlock(groupStartBlock()); + blockMap[clause] = _block; + + if (previousBlock && !previousBlock->isTerminated()) + previousBlock->JUMP(_block); + + for (StatementList *it2 = clause->statements; it2; it2 = it2->next) + statement(it2->statement); + + previousBlock = _block; + } + + leaveLoop(); + + _block->JUMP(switchend); + + _block = switchcond; + for (CaseClauses *it = ast->block->clauses; it; it = it->next) { + CaseClause *clause = it->clause; + Result rhs = expression(clause->expression); + V4IR::BasicBlock *iftrue = blockMap[clause]; + V4IR::BasicBlock *iffalse = _function->newBasicBlock(groupStartBlock()); + cjump(binop(V4IR::OpStrictEqual, *lhs, *rhs), iftrue, iffalse); + _block = iffalse; + } + + for (CaseClauses *it = ast->block->moreClauses; it; it = it->next) { + CaseClause *clause = it->clause; + Result rhs = expression(clause->expression); + V4IR::BasicBlock *iftrue = blockMap[clause]; + V4IR::BasicBlock *iffalse = _function->newBasicBlock(groupStartBlock()); + cjump(binop(V4IR::OpStrictEqual, *lhs, *rhs), iftrue, iffalse); + _block = iffalse; + } + + if (ast->block->defaultClause) { + _block->JUMP(blockMap[ast->block->defaultClause]); + } + } + + _block->JUMP(switchend); + + _block = switchend; + return false; +} + +bool Codegen::visit(ThrowStatement *ast) +{ + Result expr = expression(ast->expression); + move(_block->TEMP(_returnAddress), *expr); + _block->JUMP(_throwBlock); + return false; +} + +bool Codegen::visit(TryStatement *ast) +{ + _function->hasTry = true; + + if (_function->isStrict && ast->catchExpression && + (ast->catchExpression->name == QLatin1String("eval") || ast->catchExpression->name == QLatin1String("arguments"))) + throwSyntaxError(ast->catchExpression->identifierToken, QCoreApplication::translate("qv4codegen", "Catch variable name may not be eval or arguments in strict mode")); + + V4IR::BasicBlock *tryBody = _function->newBasicBlock(groupStartBlock()); + V4IR::BasicBlock *catchBody = _function->newBasicBlock(groupStartBlock()); + // We always need a finally body to clean up the exception handler + V4IR::BasicBlock *finallyBody = _function->newBasicBlock(groupStartBlock()); + + V4IR::BasicBlock *throwBlock = _function->newBasicBlock(groupStartBlock()); + V4IR::ExprList *throwArgs = _function->New(); + throwArgs->expr = throwBlock->TEMP(_returnAddress); + throwBlock->EXP(throwBlock->CALL(throwBlock->NAME(V4IR::Name::builtin_throw, /*line*/0, /*column*/0), throwArgs)); + throwBlock->JUMP(catchBody); + qSwap(_throwBlock, throwBlock); + + int hasException = _block->newTemp(); + move(_block->TEMP(hasException), _block->CONST(V4IR::BoolType, false)); + + // Pass the hidden "needRethrow" TEMP to the + // builtin_delete_exception_handler, in order to have those TEMPs alive for + // the duration of the exception handling block. + V4IR::ExprList *finishTryArgs = _function->New(); + finishTryArgs->init(_block->TEMP(hasException)); + + ScopeAndFinally tcf(_scopeAndFinally, ast->finallyExpression, finishTryArgs); + _scopeAndFinally = &tcf; + + int exception_to_rethrow = _block->newTemp(); + + _block->TRY(tryBody, catchBody, + ast->catchExpression ? ast->catchExpression->name.toString() : QString(), + _block->TEMP(exception_to_rethrow)); + + _block = tryBody; + statement(ast->statement); + _block->JUMP(finallyBody); + + _block = catchBody; + + if (ast->catchExpression) { + // check if an exception got thrown within catch. Go to finally + // and then rethrow + V4IR::BasicBlock *b = _function->newBasicBlock(groupStartBlock()); + _block->CJUMP(_block->TEMP(hasException), finallyBody, b); + _block = b; + } + + move(_block->TEMP(hasException), _block->CONST(V4IR::BoolType, true)); + + if (ast->catchExpression) { + ++_function->insideWithOrCatch; + { + ScopeAndFinally scope(_scopeAndFinally, ScopeAndFinally::CatchScope); + _scopeAndFinally = &scope; + statement(ast->catchExpression->statement); + _scopeAndFinally = scope.parent; + } + --_function->insideWithOrCatch; + move(_block->TEMP(hasException), _block->CONST(V4IR::BoolType, false)); + } + _block->JUMP(finallyBody); + + _scopeAndFinally = tcf.parent; + + qSwap(_throwBlock, throwBlock); + + V4IR::BasicBlock *after = _function->newBasicBlock(groupStartBlock()); + _block = finallyBody; + + _block->EXP(_block->CALL(_block->NAME(V4IR::Name::builtin_finish_try, 0, 0), finishTryArgs)); + + if (ast->finallyExpression && ast->finallyExpression->statement) + statement(ast->finallyExpression->statement); + + V4IR::BasicBlock *rethrowBlock = _function->newBasicBlock(groupStartBlock()); + _block->CJUMP(_block->TEMP(hasException), rethrowBlock, after); + _block = rethrowBlock; + move(_block->TEMP(_returnAddress), _block->TEMP(exception_to_rethrow)); + _block->JUMP(_throwBlock); + + _block = after; + + return false; +} + +void Codegen::unwindException(Codegen::ScopeAndFinally *outest) +{ + int savedDepthForWidthOrCatch = _function->insideWithOrCatch; + ScopeAndFinally *scopeAndFinally = _scopeAndFinally; + qSwap(_scopeAndFinally, scopeAndFinally); + while (_scopeAndFinally != outest) { + switch (_scopeAndFinally->type) { + case ScopeAndFinally::WithScope: + _block->EXP(_block->CALL(_block->NAME(V4IR::Name::builtin_pop_scope, 0, 0))); + // fall through + case ScopeAndFinally::CatchScope: + _scopeAndFinally = _scopeAndFinally->parent; + --_function->insideWithOrCatch; + break; + case ScopeAndFinally::TryScope: { + _block->EXP(_block->CALL(_block->NAME(V4IR::Name::builtin_finish_try, 0, 0), _scopeAndFinally->finishTryArgs)); + ScopeAndFinally *tc = _scopeAndFinally; + _scopeAndFinally = tc->parent; + if (tc->finally && tc->finally->statement) + statement(tc->finally->statement); + break; + } + } + } + qSwap(_scopeAndFinally, scopeAndFinally); + _function->insideWithOrCatch = savedDepthForWidthOrCatch; +} + +bool Codegen::visit(VariableStatement *ast) +{ + variableDeclarationList(ast->declarations); + return false; +} + +bool Codegen::visit(WhileStatement *ast) +{ + V4IR::BasicBlock *whilecond = _function->newBasicBlock(groupStartBlock()); + V4IR::BasicBlock *whilebody = _function->newBasicBlock(whilecond); + V4IR::BasicBlock *whileend = _function->newBasicBlock(groupStartBlock()); + + enterLoop(ast, whilecond, whileend, whilecond); + + _block->JUMP(whilecond); + _block = whilecond; + condition(ast->expression, whilebody, whileend); + + _block = whilebody; + statement(ast->statement); + _block->JUMP(whilecond); + + _block = whileend; + leaveLoop(); + + return false; +} + +bool Codegen::visit(WithStatement *ast) +{ + _function->hasWith = true; + + V4IR::BasicBlock *withBlock = _function->newBasicBlock(groupStartBlock()); + + _block->JUMP(withBlock); + _block = withBlock; + int withObject = _block->newTemp(); + _block->MOVE(_block->TEMP(withObject), *expression(ast->expression)); + V4IR::ExprList *args = _function->New(); + args->init(_block->TEMP(withObject)); + _block->EXP(_block->CALL(_block->NAME(V4IR::Name::builtin_push_with_scope, 0, 0), args)); + + ++_function->insideWithOrCatch; + { + ScopeAndFinally scope(_scopeAndFinally); + _scopeAndFinally = &scope; + statement(ast->statement); + _scopeAndFinally = scope.parent; + } + --_function->insideWithOrCatch; + _block->EXP(_block->CALL(_block->NAME(V4IR::Name::builtin_pop_scope, 0, 0), 0)); + + V4IR::BasicBlock *next = _function->newBasicBlock(groupStartBlock()); + _block->JUMP(next); + _block = next; + + return false; +} + +bool Codegen::visit(UiArrayBinding *) +{ + assert(!"not implemented"); + return false; +} + +bool Codegen::visit(UiObjectBinding *) +{ + assert(!"not implemented"); + return false; +} + +bool Codegen::visit(UiObjectDefinition *) +{ + assert(!"not implemented"); + return false; +} + +bool Codegen::visit(UiPublicMember *) +{ + assert(!"not implemented"); + return false; +} + +bool Codegen::visit(UiScriptBinding *) +{ + assert(!"not implemented"); + return false; +} + +bool Codegen::visit(UiSourceElement *) +{ + assert(!"not implemented"); + return false; +} + +void Codegen::throwSyntaxErrorOnEvalOrArgumentsInStrictMode(V4IR::Expr *expr, const SourceLocation& loc) +{ + if (!_env->isStrict) + return; + V4IR::Name *n = expr->asName(); + if (!n) + return; + if (*n->id == QLatin1String("eval") || *n->id == QLatin1String("arguments")) + throwSyntaxError(loc, QCoreApplication::translate("qv4codegen", "Variable name may not be eval or arguments in strict mode")); +} + +void Codegen::throwSyntaxError(const SourceLocation &loc, const QString &detail) +{ + QV4::DiagnosticMessage *msg = new QV4::DiagnosticMessage; + msg->fileName = _fileName; + msg->offset = loc.begin(); + msg->startLine = loc.startLine; + msg->startColumn = loc.startColumn; + msg->message = detail; + if (_context) + _context->throwSyntaxError(msg); + else if (_errorHandler) + _errorHandler->syntaxError(msg); + else + Q_ASSERT(!"No error handler available."); +} + +void Codegen::throwReferenceError(const SourceLocation &loc, const QString &detail) +{ + if (_context) + _context->throwReferenceError(QV4::Value::fromString(_context, detail), _fileName, loc.startLine); + else if (_errorHandler) + throwSyntaxError(loc, detail); + else + Q_ASSERT(!"No error handler available."); +} diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h new file mode 100644 index 0000000000..75ca915a13 --- /dev/null +++ b/src/qml/compiler/qv4codegen_p.h @@ -0,0 +1,451 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4CODEGEN_P_H +#define QV4CODEGEN_P_H + +#include "private/qv4global_p.h" +#include "qv4jsir_p.h" +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +namespace QV4 { +struct DiagnosticMessage; +struct ExecutionContext; +} + +namespace QQmlJS { +namespace AST { +class UiParameterList; +} + + + +class ErrorHandler +{ +public: + virtual void syntaxError(QV4::DiagnosticMessage *message) = 0; +}; + +class Q_QML_EXPORT Codegen: protected AST::Visitor +{ +public: + Codegen(QV4::ExecutionContext *ctx, bool strict); + Codegen(ErrorHandler *errorHandler, bool strictMode); + + enum Mode { + GlobalCode, + EvalCode, + FunctionCode, + QmlBinding + }; + + V4IR::Function *operator()(const QString &fileName, + const QString &sourceCode, + AST::Program *ast, + V4IR::Module *module, + Mode mode = GlobalCode, + const QStringList &inheritedLocals = QStringList()); + V4IR::Function *operator()(const QString &fileName, + const QString &sourceCode, + AST::FunctionExpression *ast, + V4IR::Module *module); + +protected: + enum Format { ex, cx, nx }; + struct Result { + V4IR::Expr *code; + V4IR::BasicBlock *iftrue; + V4IR::BasicBlock *iffalse; + Format format; + Format requested; + + explicit Result(Format requested = ex) + : code(0) + , iftrue(0) + , iffalse(0) + , format(ex) + , requested(requested) {} + + explicit Result(V4IR::BasicBlock *iftrue, V4IR::BasicBlock *iffalse) + : code(0) + , iftrue(iftrue) + , iffalse(iffalse) + , format(ex) + , requested(cx) {} + + inline V4IR::Expr *operator*() const { Q_ASSERT(format == ex); return code; } + inline V4IR::Expr *operator->() const { Q_ASSERT(format == ex); return code; } + + bool accept(Format f) + { + if (requested == f) { + format = f; + return true; + } + return false; + } + }; + + struct Environment { + Environment *parent; + + enum MemberType { + UndefinedMember, + VariableDefinition, + VariableDeclaration, + FunctionDefinition + }; + struct Member { + MemberType type; + int index; + AST::FunctionExpression *function; + }; + typedef QMap MemberMap; + + MemberMap members; + AST::FormalParameterList *formals; + int maxNumberOfArguments; + bool hasDirectEval; + bool hasNestedFunctions; + bool isStrict; + bool isNamedFunctionExpression; + enum UsesArgumentsObject { + ArgumentsObjectUnknown, + ArgumentsObjectNotUsed, + ArgumentsObjectUsed + }; + + UsesArgumentsObject usesArgumentsObject; + + Environment(Environment *parent) + : parent(parent) + , formals(0) + , maxNumberOfArguments(0) + , hasDirectEval(false) + , hasNestedFunctions(false) + , isStrict(false) + , isNamedFunctionExpression(false) + , usesArgumentsObject(ArgumentsObjectUnknown) + { + if (parent && parent->isStrict) + isStrict = true; + } + + int findMember(const QString &name) const + { + MemberMap::const_iterator it = members.find(name); + if (it == members.end()) + return -1; + assert((*it).index != -1 || !parent); + return (*it).index; + } + + bool lookupMember(const QString &name, Environment **scope, int *index, int *distance) + { + Environment *it = this; + *distance = 0; + for (; it; it = it->parent, ++(*distance)) { + int idx = it->findMember(name); + if (idx != -1) { + *scope = it; + *index = idx; + return true; + } + } + return false; + } + + void enter(const QString &name, MemberType type, AST::FunctionExpression *function = 0) + { + if (! name.isEmpty()) { + if (type != FunctionDefinition) { + for (AST::FormalParameterList *it = formals; it; it = it->next) + if (it->name == name) + return; + } + MemberMap::iterator it = members.find(name); + if (it == members.end()) { + Member m; + m.index = -1; + m.type = type; + m.function = function; + members.insert(name, m); + } else { + if ((*it).type <= type) { + (*it).type = type; + (*it).function = function; + } + } + } + } + }; + + Environment *newEnvironment(AST::Node *node, Environment *parent) + { + Environment *env = new Environment(parent); + _envMap.insert(node, env); + return env; + } + + struct UiMember { + }; + + struct ScopeAndFinally { + enum ScopeType { + WithScope, + TryScope, + CatchScope + }; + + ScopeAndFinally *parent; + AST::Finally *finally; + V4IR::ExprList *finishTryArgs; + ScopeType type; + + ScopeAndFinally(ScopeAndFinally *parent, ScopeType t = WithScope) : parent(parent), finally(0), finishTryArgs(0), type(t) {} + ScopeAndFinally(ScopeAndFinally *parent, AST::Finally *finally, V4IR::ExprList *finishTryArgs) + : parent(parent), finally(finally), finishTryArgs(finishTryArgs), type(TryScope) + {} + }; + + struct Loop { + AST::LabelledStatement *labelledStatement; + AST::Statement *node; + V4IR::BasicBlock *groupStartBlock; + V4IR::BasicBlock *breakBlock; + V4IR::BasicBlock *continueBlock; + Loop *parent; + ScopeAndFinally *scopeAndFinally; + + Loop(AST::Statement *node, V4IR::BasicBlock *groupStartBlock, V4IR::BasicBlock *breakBlock, V4IR::BasicBlock *continueBlock, Loop *parent) + : labelledStatement(0), node(node), groupStartBlock(groupStartBlock), breakBlock(breakBlock), continueBlock(continueBlock), parent(parent) {} + }; + + void enterEnvironment(AST::Node *node); + void leaveEnvironment(); + + void enterLoop(AST::Statement *node, V4IR::BasicBlock *startBlock, V4IR::BasicBlock *breakBlock, V4IR::BasicBlock *continueBlock); + void leaveLoop(); + V4IR::BasicBlock *groupStartBlock() const + { + for (Loop *it = _loop; it; it = it->parent) + if (it->groupStartBlock) + return it->groupStartBlock; + return 0; + } + + V4IR::Expr *member(V4IR::Expr *base, const QString *name); + V4IR::Expr *subscript(V4IR::Expr *base, V4IR::Expr *index); + V4IR::Expr *argument(V4IR::Expr *expr); + V4IR::Expr *reference(V4IR::Expr *expr); + V4IR::Expr *unop(V4IR::AluOp op, V4IR::Expr *expr); + V4IR::Expr *binop(V4IR::AluOp op, V4IR::Expr *left, V4IR::Expr *right); + V4IR::Expr *call(V4IR::Expr *base, V4IR::ExprList *args); + void move(V4IR::Expr *target, V4IR::Expr *source, V4IR::AluOp op = V4IR::OpInvalid); + void cjump(V4IR::Expr *cond, V4IR::BasicBlock *iftrue, V4IR::BasicBlock *iffalse); + + V4IR::Function *defineFunction(const QString &name, AST::Node *ast, + AST::FormalParameterList *formals, + AST::SourceElements *body, + Mode mode = FunctionCode, + const QStringList &inheritedLocals = QStringList()); + + void unwindException(ScopeAndFinally *outest); + + void statement(AST::Statement *ast); + void statement(AST::ExpressionNode *ast); + void condition(AST::ExpressionNode *ast, V4IR::BasicBlock *iftrue, V4IR::BasicBlock *iffalse); + Result expression(AST::ExpressionNode *ast); + QString propertyName(AST::PropertyName *ast); + Result sourceElement(AST::SourceElement *ast); + UiMember uiObjectMember(AST::UiObjectMember *ast); + + void accept(AST::Node *node); + + void functionBody(AST::FunctionBody *ast); + void program(AST::Program *ast); + void sourceElements(AST::SourceElements *ast); + void variableDeclaration(AST::VariableDeclaration *ast); + void variableDeclarationList(AST::VariableDeclarationList *ast); + + V4IR::Expr *identifier(const QString &name, int line = 0, int col = 0); + + // nodes + virtual bool visit(AST::ArgumentList *ast); + virtual bool visit(AST::CaseBlock *ast); + virtual bool visit(AST::CaseClause *ast); + virtual bool visit(AST::CaseClauses *ast); + virtual bool visit(AST::Catch *ast); + virtual bool visit(AST::DefaultClause *ast); + virtual bool visit(AST::ElementList *ast); + virtual bool visit(AST::Elision *ast); + virtual bool visit(AST::Finally *ast); + virtual bool visit(AST::FormalParameterList *ast); + virtual bool visit(AST::FunctionBody *ast); + virtual bool visit(AST::Program *ast); + virtual bool visit(AST::PropertyNameAndValue *ast); + virtual bool visit(AST::PropertyAssignmentList *ast); + virtual bool visit(AST::PropertyGetterSetter *ast); + virtual bool visit(AST::SourceElements *ast); + virtual bool visit(AST::StatementList *ast); + virtual bool visit(AST::UiArrayMemberList *ast); + virtual bool visit(AST::UiImport *ast); + virtual bool visit(AST::UiImportList *ast); + virtual bool visit(AST::UiObjectInitializer *ast); + virtual bool visit(AST::UiObjectMemberList *ast); + virtual bool visit(AST::UiParameterList *ast); + virtual bool visit(AST::UiProgram *ast); + virtual bool visit(AST::UiQualifiedId *ast); + virtual bool visit(AST::VariableDeclaration *ast); + virtual bool visit(AST::VariableDeclarationList *ast); + + // expressions + virtual bool visit(AST::Expression *ast); + virtual bool visit(AST::ArrayLiteral *ast); + virtual bool visit(AST::ArrayMemberExpression *ast); + virtual bool visit(AST::BinaryExpression *ast); + virtual bool visit(AST::CallExpression *ast); + virtual bool visit(AST::ConditionalExpression *ast); + virtual bool visit(AST::DeleteExpression *ast); + virtual bool visit(AST::FalseLiteral *ast); + virtual bool visit(AST::FieldMemberExpression *ast); + virtual bool visit(AST::FunctionExpression *ast); + virtual bool visit(AST::IdentifierExpression *ast); + virtual bool visit(AST::NestedExpression *ast); + virtual bool visit(AST::NewExpression *ast); + virtual bool visit(AST::NewMemberExpression *ast); + virtual bool visit(AST::NotExpression *ast); + virtual bool visit(AST::NullExpression *ast); + virtual bool visit(AST::NumericLiteral *ast); + virtual bool visit(AST::ObjectLiteral *ast); + virtual bool visit(AST::PostDecrementExpression *ast); + virtual bool visit(AST::PostIncrementExpression *ast); + virtual bool visit(AST::PreDecrementExpression *ast); + virtual bool visit(AST::PreIncrementExpression *ast); + virtual bool visit(AST::RegExpLiteral *ast); + virtual bool visit(AST::StringLiteral *ast); + virtual bool visit(AST::ThisExpression *ast); + virtual bool visit(AST::TildeExpression *ast); + virtual bool visit(AST::TrueLiteral *ast); + virtual bool visit(AST::TypeOfExpression *ast); + virtual bool visit(AST::UnaryMinusExpression *ast); + virtual bool visit(AST::UnaryPlusExpression *ast); + virtual bool visit(AST::VoidExpression *ast); + virtual bool visit(AST::FunctionDeclaration *ast); + + // property names + virtual bool visit(AST::IdentifierPropertyName *ast); + virtual bool visit(AST::NumericLiteralPropertyName *ast); + virtual bool visit(AST::StringLiteralPropertyName *ast); + + // source elements + virtual bool visit(AST::FunctionSourceElement *ast); + virtual bool visit(AST::StatementSourceElement *ast); + + // statements + virtual bool visit(AST::Block *ast); + virtual bool visit(AST::BreakStatement *ast); + virtual bool visit(AST::ContinueStatement *ast); + virtual bool visit(AST::DebuggerStatement *ast); + virtual bool visit(AST::DoWhileStatement *ast); + virtual bool visit(AST::EmptyStatement *ast); + virtual bool visit(AST::ExpressionStatement *ast); + virtual bool visit(AST::ForEachStatement *ast); + virtual bool visit(AST::ForStatement *ast); + virtual bool visit(AST::IfStatement *ast); + virtual bool visit(AST::LabelledStatement *ast); + virtual bool visit(AST::LocalForEachStatement *ast); + virtual bool visit(AST::LocalForStatement *ast); + virtual bool visit(AST::ReturnStatement *ast); + virtual bool visit(AST::SwitchStatement *ast); + virtual bool visit(AST::ThrowStatement *ast); + virtual bool visit(AST::TryStatement *ast); + virtual bool visit(AST::VariableStatement *ast); + virtual bool visit(AST::WhileStatement *ast); + virtual bool visit(AST::WithStatement *ast); + + // ui object members + virtual bool visit(AST::UiArrayBinding *ast); + virtual bool visit(AST::UiObjectBinding *ast); + virtual bool visit(AST::UiObjectDefinition *ast); + virtual bool visit(AST::UiPublicMember *ast); + virtual bool visit(AST::UiScriptBinding *ast); + virtual bool visit(AST::UiSourceElement *ast); + + void throwSyntaxErrorOnEvalOrArgumentsInStrictMode(V4IR::Expr* expr, const AST::SourceLocation &loc); + + void throwSyntaxError(const AST::SourceLocation &loc, const QString &detail); + void throwReferenceError(const AST::SourceLocation &loc, const QString &detail); + +private: + QString _fileName; + Result _expr; + QString _property; + UiMember _uiMember; + V4IR::Module *_module; + V4IR::Function *_function; + V4IR::BasicBlock *_block; + V4IR::BasicBlock *_exitBlock; + V4IR::BasicBlock *_throwBlock; + unsigned _returnAddress; + Mode _mode; + Environment *_env; + Loop *_loop; + AST::LabelledStatement *_labelledStatement; + ScopeAndFinally *_scopeAndFinally; + QHash _envMap; + QHash _functionMap; + QV4::ExecutionContext *_context; + bool _strictMode; + ErrorHandler *_errorHandler; + + class ScanFunctions; +}; + +} + +QT_END_NAMESPACE + +#endif // QV4CODEGEN_P_H diff --git a/src/qml/compiler/qv4instr_moth.cpp b/src/qml/compiler/qv4instr_moth.cpp new file mode 100644 index 0000000000..ec68ede72d --- /dev/null +++ b/src/qml/compiler/qv4instr_moth.cpp @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4instr_moth_p.h" + +using namespace QQmlJS; +using namespace QQmlJS::Moth; + +int Instr::size(Type type) +{ +#define MOTH_RETURN_INSTR_SIZE(I, FMT) case I: return InstrMeta<(int)I>::Size; + switch (type) { + FOR_EACH_MOTH_INSTR(MOTH_RETURN_INSTR_SIZE) + default: return 0; + } +#undef MOTH_RETURN_INSTR_SIZE +} + diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h new file mode 100644 index 0000000000..ac7196e2d1 --- /dev/null +++ b/src/qml/compiler/qv4instr_moth_p.h @@ -0,0 +1,621 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4INSTR_MOTH_P_H +#define QV4INSTR_MOTH_P_H + +#include +#include + +QT_BEGIN_NAMESPACE + +#define FOR_EACH_MOTH_INSTR(F) \ + F(Ret, ret) \ + F(LoadValue, loadValue) \ + F(LoadClosure, loadClosure) \ + F(MoveTemp, moveTemp) \ + F(LoadName, loadName) \ + F(StoreName, storeName) \ + F(LoadElement, loadElement) \ + F(StoreElement, storeElement) \ + F(LoadProperty, loadProperty) \ + F(StoreProperty, storeProperty) \ + F(Push, push) \ + F(EnterTry, enterTry) \ + F(CallValue, callValue) \ + F(CallProperty, callProperty) \ + F(CallElement, callElement) \ + F(CallActivationProperty, callActivationProperty) \ + F(CallBuiltinThrow, callBuiltinThrow) \ + F(CallBuiltinFinishTry, callBuiltinFinishTry) \ + F(CallBuiltinPushScope, callBuiltinPushScope) \ + F(CallBuiltinPopScope, callBuiltinPopScope) \ + F(CallBuiltinForeachIteratorObject, callBuiltinForeachIteratorObject) \ + F(CallBuiltinForeachNextPropertyName, callBuiltinForeachNextPropertyName) \ + F(CallBuiltinDeleteMember, callBuiltinDeleteMember) \ + F(CallBuiltinDeleteSubscript, callBuiltinDeleteSubscript) \ + F(CallBuiltinDeleteName, callBuiltinDeleteName) \ + F(CallBuiltinTypeofMember, callBuiltinTypeofMember) \ + F(CallBuiltinTypeofSubscript, callBuiltinTypeofSubscript) \ + F(CallBuiltinTypeofName, callBuiltinTypeofName) \ + F(CallBuiltinTypeofValue, callBuiltinTypeofValue) \ + F(CallBuiltinPostIncMember, callBuiltinPostIncMember) \ + F(CallBuiltinPostIncSubscript, callBuiltinPostIncSubscript) \ + F(CallBuiltinPostIncName, callBuiltinPostIncName) \ + F(CallBuiltinPostIncValue, callBuiltinPostIncValue) \ + F(CallBuiltinPostDecMember, callBuiltinPostDecMember) \ + F(CallBuiltinPostDecSubscript, callBuiltinPostDecSubscript) \ + F(CallBuiltinPostDecName, callBuiltinPostDecName) \ + F(CallBuiltinPostDecValue, callBuiltinPostDecValue) \ + F(CallBuiltinDeclareVar, callBuiltinDeclareVar) \ + F(CallBuiltinDefineGetterSetter, callBuiltinDefineGetterSetter) \ + F(CallBuiltinDefineProperty, callBuiltinDefineProperty) \ + F(CallBuiltinDefineArray, callBuiltinDefineArray) \ + F(CallBuiltinDefineObjectLiteral, callBuiltinDefineObjectLiteral) \ + F(CreateValue, createValue) \ + F(CreateProperty, createProperty) \ + F(CreateActivationProperty, createActivationProperty) \ + F(Jump, jump) \ + F(CJump, cjump) \ + F(Unop, unop) \ + F(Binop, binop) \ + F(BinopContext, binopContext) \ + F(AddNumberParams, addNumberParams) \ + F(MulNumberParams, mulNumberParams) \ + F(SubNumberParams, subNumberParams) \ + F(LoadThis, loadThis) \ + F(InplaceElementOp, inplaceElementOp) \ + F(InplaceMemberOp, inplaceMemberOp) \ + F(InplaceNameOp, inplaceNameOp) + +#if defined(Q_CC_GNU) && (!defined(Q_CC_INTEL) || __INTEL_COMPILER >= 1200) +# define MOTH_THREADED_INTERPRETER +#endif + +#define MOTH_INSTR_ALIGN_MASK (Q_ALIGNOF(QQmlJS::Moth::Instr) - 1) + +#ifdef MOTH_THREADED_INTERPRETER +# define MOTH_INSTR_HEADER void *code; \ + unsigned int breakPoint : 1; +#else +# define MOTH_INSTR_HEADER quint8 instructionType; \ + unsigned int breakPoint : 1; +#endif + +#define MOTH_INSTR_ENUM(I, FMT) I, +#define MOTH_INSTR_SIZE(I, FMT) ((sizeof(QQmlJS::Moth::Instr::instr_##FMT) + MOTH_INSTR_ALIGN_MASK) & ~MOTH_INSTR_ALIGN_MASK) + + +namespace QQmlJS { +namespace Moth { + +union Instr +{ + struct Param { + enum { + ValueType = 0, + ArgumentType = 1, + LocalType = 2, + TempType = 3, + ScopedLocalType = 4 + }; + QV4::Value value; + unsigned type : 3; + unsigned scope : 29; + unsigned index; + + bool isValue() const { return type == ValueType; } + bool isArgument() const { return type == ArgumentType; } + bool isLocal() const { return type == LocalType; } + bool isTemp() const { return type == TempType; } + bool isScopedLocal() const { return type == ScopedLocalType; } + + static Param createValue(const QV4::Value &v) + { + Param p; + p.type = ValueType; + p.scope = 0; + p.value = v; + return p; + } + + static Param createArgument(unsigned idx, uint scope) + { + Param p; + p.type = ArgumentType; + p.scope = scope; + p.index = idx; + return p; + } + + static Param createLocal(unsigned idx) + { + Param p; + p.type = LocalType; + p.scope = 0; + p.index = idx; + return p; + } + + static Param createTemp(unsigned idx) + { + Param p; + p.type = TempType; + p.scope = 0; + p.index = idx; + return p; + } + + static Param createScopedLocal(unsigned idx, uint scope) + { + Param p; + p.type = ScopedLocalType; + p.scope = scope; + p.index = idx; + return p; + } + + inline bool operator==(const Param &other) const + { return type == other.type && scope == other.scope && index == other.index; } + + inline bool operator!=(const Param &other) const + { return !(*this == other); } + }; + + enum Type { + FOR_EACH_MOTH_INSTR(MOTH_INSTR_ENUM) + }; + + struct instr_common { + MOTH_INSTR_HEADER + }; + struct instr_ret { + MOTH_INSTR_HEADER + Param result; + }; + struct instr_loadValue { + MOTH_INSTR_HEADER + Param value; + Param result; + }; + struct instr_moveTemp { + MOTH_INSTR_HEADER + Param source; + Param result; + }; + struct instr_loadClosure { + MOTH_INSTR_HEADER + QV4::Function *value; + Param result; + }; + struct instr_loadName { + MOTH_INSTR_HEADER + QV4::String *name; + Param result; + }; + struct instr_storeName { + MOTH_INSTR_HEADER + QV4::String *name; + Param source; + }; + struct instr_loadProperty { + MOTH_INSTR_HEADER + QV4::String *name; + Param base; + Param result; + }; + struct instr_storeProperty { + MOTH_INSTR_HEADER + QV4::String *name; + Param base; + Param source; + }; + struct instr_loadElement { + MOTH_INSTR_HEADER + Param base; + Param index; + Param result; + }; + struct instr_storeElement { + MOTH_INSTR_HEADER + Param base; + Param index; + Param source; + }; + struct instr_push { + MOTH_INSTR_HEADER + quint32 value; + }; + struct instr_enterTry { + MOTH_INSTR_HEADER + ptrdiff_t tryOffset; + ptrdiff_t catchOffset; + QV4::String *exceptionVarName; + Param exceptionVar; + }; + struct instr_callValue { + MOTH_INSTR_HEADER + quint32 argc; + quint32 args; + Param dest; + Param result; + }; + struct instr_callProperty { + MOTH_INSTR_HEADER + QV4::String *name; + quint32 argc; + quint32 args; + Param base; + Param result; + }; + struct instr_callElement { + MOTH_INSTR_HEADER + Param base; + Param index; + quint32 argc; + quint32 args; + Param result; + }; + struct instr_callActivationProperty { + MOTH_INSTR_HEADER + QV4::String *name; + quint32 argc; + quint32 args; + Param result; + }; + struct instr_callBuiltinThrow { + MOTH_INSTR_HEADER + Param arg; + }; + struct instr_callBuiltinFinishTry { + MOTH_INSTR_HEADER + }; + struct instr_callBuiltinPushScope { + MOTH_INSTR_HEADER + Param arg; + }; + struct instr_callBuiltinPopScope { + MOTH_INSTR_HEADER + }; + struct instr_callBuiltinForeachIteratorObject { + MOTH_INSTR_HEADER + Param arg; + Param result; + }; + struct instr_callBuiltinForeachNextPropertyName { + MOTH_INSTR_HEADER + Param arg; + Param result; + }; + struct instr_callBuiltinDeleteMember { + MOTH_INSTR_HEADER + QV4::String *member; + Param base; + Param result; + }; + struct instr_callBuiltinDeleteSubscript { + MOTH_INSTR_HEADER + Param base; + Param index; + Param result; + }; + struct instr_callBuiltinDeleteName { + MOTH_INSTR_HEADER + QV4::String *name; + Param result; + }; + struct instr_callBuiltinTypeofMember { + MOTH_INSTR_HEADER + QV4::String *member; + Param base; + Param result; + }; + struct instr_callBuiltinTypeofSubscript { + MOTH_INSTR_HEADER + Param base; + Param index; + Param result; + }; + struct instr_callBuiltinTypeofName { + MOTH_INSTR_HEADER + QV4::String *name; + Param result; + }; + struct instr_callBuiltinTypeofValue { + MOTH_INSTR_HEADER + Param value; + Param result; + }; + struct instr_callBuiltinPostIncMember { + MOTH_INSTR_HEADER + Param base; + QV4::String *member; + Param result; + }; + struct instr_callBuiltinPostIncSubscript { + MOTH_INSTR_HEADER + Param base; + Param index; + Param result; + }; + struct instr_callBuiltinPostIncName { + MOTH_INSTR_HEADER + QV4::String *name; + Param result; + }; + struct instr_callBuiltinPostIncValue { + MOTH_INSTR_HEADER + Param value; + Param result; + }; + struct instr_callBuiltinPostDecMember { + MOTH_INSTR_HEADER + Param base; + QV4::String *member; + Param result; + }; + struct instr_callBuiltinPostDecSubscript { + MOTH_INSTR_HEADER + Param base; + Param index; + Param result; + }; + struct instr_callBuiltinPostDecName { + MOTH_INSTR_HEADER + QV4::String *name; + Param result; + }; + struct instr_callBuiltinPostDecValue { + MOTH_INSTR_HEADER + Param value; + Param result; + }; + struct instr_callBuiltinDeclareVar { + MOTH_INSTR_HEADER + QV4::String *varName; + bool isDeletable; + }; + struct instr_callBuiltinDefineGetterSetter { + MOTH_INSTR_HEADER + QV4::String *name; + Param object; + Param getter; + Param setter; + }; + struct instr_callBuiltinDefineProperty { + MOTH_INSTR_HEADER + QV4::String *name; + Param object; + Param value; + }; + struct instr_callBuiltinDefineArray { + MOTH_INSTR_HEADER + quint32 argc; + quint32 args; + Param result; + }; + struct instr_callBuiltinDefineObjectLiteral { + MOTH_INSTR_HEADER + QV4::InternalClass *internalClass; + quint32 args; + Param result; + }; + struct instr_createValue { + MOTH_INSTR_HEADER + quint32 argc; + quint32 args; + Param func; + Param result; + }; + struct instr_createProperty { + MOTH_INSTR_HEADER + QV4::String *name; + quint32 argc; + quint32 args; + Param base; + Param result; + }; + struct instr_createActivationProperty { + MOTH_INSTR_HEADER + QV4::String *name; + quint32 argc; + quint32 args; + Param result; + }; + struct instr_jump { + MOTH_INSTR_HEADER + ptrdiff_t offset; + }; + struct instr_cjump { + MOTH_INSTR_HEADER + ptrdiff_t offset; + Param condition; + }; + struct instr_unop { + MOTH_INSTR_HEADER + QV4::UnaryOpName alu; + Param source; + Param result; + }; + struct instr_binop { + MOTH_INSTR_HEADER + QV4::BinOp alu; + Param lhs; + Param rhs; + Param result; + }; + struct instr_binopContext { + MOTH_INSTR_HEADER + QV4::BinOpContext alu; + Param lhs; + Param rhs; + Param result; + }; + struct instr_addNumberParams { + MOTH_INSTR_HEADER + Param lhs; + Param rhs; + Param result; + }; + struct instr_mulNumberParams { + MOTH_INSTR_HEADER + Param lhs; + Param rhs; + Param result; + }; + struct instr_subNumberParams { + MOTH_INSTR_HEADER + Param lhs; + Param rhs; + Param result; + }; + struct instr_loadThis { + MOTH_INSTR_HEADER + Param result; + }; + struct instr_inplaceElementOp { + MOTH_INSTR_HEADER + QV4::InplaceBinOpElement alu; + Param base; + Param index; + Param source; + }; + struct instr_inplaceMemberOp { + MOTH_INSTR_HEADER + QV4::InplaceBinOpMember alu; + QV4::String *member; + Param base; + Param source; + }; + struct instr_inplaceNameOp { + MOTH_INSTR_HEADER + QV4::InplaceBinOpName alu; + QV4::String *name; + Param source; + }; + + instr_common common; + instr_ret ret; + instr_loadValue loadValue; + instr_moveTemp moveTemp; + instr_loadClosure loadClosure; + instr_loadName loadName; + instr_storeName storeName; + instr_loadElement loadElement; + instr_storeElement storeElement; + instr_loadProperty loadProperty; + instr_storeProperty storeProperty; + instr_push push; + instr_enterTry enterTry; + instr_callValue callValue; + instr_callProperty callProperty; + instr_callElement callElement; + instr_callActivationProperty callActivationProperty; + instr_callBuiltinThrow callBuiltinThrow; + instr_callBuiltinFinishTry callBuiltinFinishTry; + instr_callBuiltinPushScope callBuiltinPushScope; + instr_callBuiltinPopScope callBuiltinPopScope; + instr_callBuiltinForeachIteratorObject callBuiltinForeachIteratorObject; + instr_callBuiltinForeachNextPropertyName callBuiltinForeachNextPropertyName; + instr_callBuiltinDeleteMember callBuiltinDeleteMember; + instr_callBuiltinDeleteSubscript callBuiltinDeleteSubscript; + instr_callBuiltinDeleteName callBuiltinDeleteName; + instr_callBuiltinTypeofMember callBuiltinTypeofMember; + instr_callBuiltinTypeofSubscript callBuiltinTypeofSubscript; + instr_callBuiltinTypeofName callBuiltinTypeofName; + instr_callBuiltinTypeofValue callBuiltinTypeofValue; + instr_callBuiltinPostIncMember callBuiltinPostIncMember; + instr_callBuiltinPostIncSubscript callBuiltinPostIncSubscript; + instr_callBuiltinPostIncName callBuiltinPostIncName; + instr_callBuiltinPostIncValue callBuiltinPostIncValue; + instr_callBuiltinPostDecMember callBuiltinPostDecMember; + instr_callBuiltinPostDecSubscript callBuiltinPostDecSubscript; + instr_callBuiltinPostDecName callBuiltinPostDecName; + instr_callBuiltinPostDecValue callBuiltinPostDecValue; + instr_callBuiltinDeclareVar callBuiltinDeclareVar; + instr_callBuiltinDefineGetterSetter callBuiltinDefineGetterSetter; + instr_callBuiltinDefineProperty callBuiltinDefineProperty; + instr_callBuiltinDefineArray callBuiltinDefineArray; + instr_callBuiltinDefineObjectLiteral callBuiltinDefineObjectLiteral; + instr_createValue createValue; + instr_createProperty createProperty; + instr_createActivationProperty createActivationProperty; + instr_jump jump; + instr_cjump cjump; + instr_unop unop; + instr_binop binop; + instr_binopContext binopContext; + instr_addNumberParams addNumberParams; + instr_mulNumberParams mulNumberParams; + instr_subNumberParams subNumberParams; + instr_loadThis loadThis; + instr_inplaceElementOp inplaceElementOp; + instr_inplaceMemberOp inplaceMemberOp; + instr_inplaceNameOp inplaceNameOp; + + static int size(Type type); +}; + +template +struct InstrMeta { +}; + +#define MOTH_INSTR_META_TEMPLATE(I, FMT) \ + template<> struct InstrMeta<(int)Instr::I> { \ + enum { Size = MOTH_INSTR_SIZE(I, FMT) }; \ + typedef Instr::instr_##FMT DataType; \ + static const DataType &data(const Instr &instr) { return instr.FMT; } \ + static void setData(Instr &instr, const DataType &v) { instr.FMT = v; } \ + }; +FOR_EACH_MOTH_INSTR(MOTH_INSTR_META_TEMPLATE); +#undef MOTH_INSTR_META_TEMPLATE + +template +class InstrData : public InstrMeta::DataType +{ +}; + +} // namespace Moth +} // namespace QQmlJS + +QT_END_NAMESPACE + +#endif // QV4INSTR_MOTH_P_H diff --git a/src/qml/compiler/qv4isel_masm.cpp b/src/qml/compiler/qv4isel_masm.cpp new file mode 100644 index 0000000000..4317ba0d54 --- /dev/null +++ b/src/qml/compiler/qv4isel_masm.cpp @@ -0,0 +1,1466 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4isel_masm_p.h" +#include "qv4runtime_p.h" +#include "qv4object_p.h" +#include "qv4functionobject_p.h" +#include "qv4regexpobject_p.h" +#include "qv4unwindhelper_p.h" +#include "qv4lookup_p.h" +#include "qv4function_p.h" +#include "qv4ssa_p.h" +#include "qv4exception_p.h" + +#include +#include + +#include +#include + +#if USE(UDIS86) +# include +#endif + +using namespace QQmlJS; +using namespace QQmlJS::MASM; +using namespace QV4; + +namespace { +class ConvertTemps: protected V4IR::StmtVisitor, protected V4IR::ExprVisitor +{ + int _nextFreeStackSlot; + QHash _stackSlotForTemp; + + void renumber(V4IR::Temp *t) + { + if (t->kind != V4IR::Temp::VirtualRegister) + return; + + int stackSlot = _stackSlotForTemp.value(*t, -1); + if (stackSlot == -1) { + stackSlot = _nextFreeStackSlot++; + _stackSlotForTemp[*t] = stackSlot; + } + + t->kind = V4IR::Temp::StackSlot; + t->index = stackSlot; + } + +public: + ConvertTemps() + : _nextFreeStackSlot(0) + {} + + void toStackSlots(V4IR::Function *function) + { + _stackSlotForTemp.reserve(function->tempCount); + + foreach (V4IR::BasicBlock *bb, function->basicBlocks) + foreach (V4IR::Stmt *s, bb->statements) + s->accept(this); + + function->tempCount = _nextFreeStackSlot; + } + +protected: + virtual void visitConst(V4IR::Const *) {} + virtual void visitString(V4IR::String *) {} + virtual void visitRegExp(V4IR::RegExp *) {} + virtual void visitName(V4IR::Name *) {} + virtual void visitTemp(V4IR::Temp *e) { renumber(e); } + virtual void visitClosure(V4IR::Closure *) {} + virtual void visitConvert(V4IR::Convert *e) { e->expr->accept(this); } + virtual void visitUnop(V4IR::Unop *e) { e->expr->accept(this); } + virtual void visitBinop(V4IR::Binop *e) { e->left->accept(this); e->right->accept(this); } + virtual void visitCall(V4IR::Call *e) { + e->base->accept(this); + for (V4IR::ExprList *it = e->args; it; it = it->next) + it->expr->accept(this); + } + virtual void visitNew(V4IR::New *e) { + e->base->accept(this); + for (V4IR::ExprList *it = e->args; it; it = it->next) + it->expr->accept(this); + } + virtual void visitSubscript(V4IR::Subscript *e) { e->base->accept(this); e->index->accept(this); } + virtual void visitMember(V4IR::Member *e) { e->base->accept(this); } + virtual void visitExp(V4IR::Exp *s) { s->expr->accept(this); } + virtual void visitMove(V4IR::Move *s) { s->target->accept(this); s->source->accept(this); } + virtual void visitJump(V4IR::Jump *) {} + virtual void visitCJump(V4IR::CJump *s) { s->cond->accept(this); } + virtual void visitRet(V4IR::Ret *s) { s->expr->accept(this); } + virtual void visitTry(V4IR::Try *s) { s->exceptionVar->accept(this); } + virtual void visitPhi(V4IR::Phi *) { Q_UNREACHABLE(); } +}; +} // anonymous namespace + +/* Platform/Calling convention/Architecture specific section */ + +#if CPU(X86_64) +static const Assembler::RegisterID calleeSavedRegisters[] = { + // Not used: JSC::X86Registers::rbx, + // Not used: JSC::X86Registers::r10, + JSC::X86Registers::r12, // LocalsRegister + // Not used: JSC::X86Registers::r13, + JSC::X86Registers::r14 // ContextRegister + // Not used: JSC::X86Registers::r15, +}; +#endif + +#if CPU(X86) +static const Assembler::RegisterID calleeSavedRegisters[] = { + // Not used: JSC::X86Registers::ebx, + JSC::X86Registers::esi, // ContextRegister + JSC::X86Registers::edi // LocalsRegister +}; +#endif + +#if CPU(ARM) +static const Assembler::RegisterID calleeSavedRegisters[] = { + // ### FIXME: remove unused registers. + // Keep these in reverse order and make sure to also edit the unwind program in + // qv4unwindhelper_p-arm.h when changing this list. + JSC::ARMRegisters::r12, + JSC::ARMRegisters::r10, + JSC::ARMRegisters::r9, + JSC::ARMRegisters::r8, + JSC::ARMRegisters::r7, + JSC::ARMRegisters::r6, + JSC::ARMRegisters::r5, + JSC::ARMRegisters::r4 +}; +#endif + +const int Assembler::calleeSavedRegisterCount = sizeof(calleeSavedRegisters) / sizeof(calleeSavedRegisters[0]); + +/* End of platform/calling convention/architecture specific section */ + + +const Assembler::VoidType Assembler::Void; + +Assembler::Assembler(V4IR::Function* function, QV4::Function *vmFunction, QV4::ExecutionEngine *engine) + : _function(function), _vmFunction(vmFunction), _engine(engine), _nextBlock(0) +{ +} + +void Assembler::registerBlock(V4IR::BasicBlock* block, V4IR::BasicBlock *nextBlock) +{ + _addrs[block] = label(); + _nextBlock = nextBlock; +} + +void Assembler::jumpToBlock(V4IR::BasicBlock* current, V4IR::BasicBlock *target) +{ + if (target != _nextBlock) + _patches[target].append(jump()); +} + +void Assembler::addPatch(V4IR::BasicBlock* targetBlock, Jump targetJump) +{ + _patches[targetBlock].append(targetJump); +} + +void Assembler::addPatch(DataLabelPtr patch, Label target) +{ + DataLabelPatch p; + p.dataLabel = patch; + p.target = target; + _dataLabelPatches.append(p); +} + +void Assembler::addPatch(DataLabelPtr patch, V4IR::BasicBlock *target) +{ + _labelPatches[target].append(patch); +} + +Assembler::Pointer Assembler::loadTempAddress(RegisterID reg, V4IR::Temp *t) +{ + int32_t offset = 0; + int scope = t->scope; + QV4::Function *f = _vmFunction; + RegisterID context = ContextRegister; + if (scope) { + loadPtr(Address(ContextRegister, offsetof(ExecutionContext, outer)), ScratchRegister); + --scope; + f = f->outer; + context = ScratchRegister; + while (scope) { + loadPtr(Address(context, offsetof(ExecutionContext, outer)), context); + f = f->outer; + --scope; + } + } + switch (t->kind) { + case V4IR::Temp::Formal: + case V4IR::Temp::ScopedFormal: { + loadPtr(Address(context, offsetof(CallContext, arguments)), reg); + offset = t->index * sizeof(Value); + } break; + case V4IR::Temp::Local: + case V4IR::Temp::ScopedLocal: { + loadPtr(Address(context, offsetof(CallContext, locals)), reg); + offset = t->index * sizeof(Value); + } break; + case V4IR::Temp::StackSlot: { + assert(t->scope == 0); + const int arg = _function->maxNumberOfArguments + t->index + 1; + offset = - sizeof(Value) * (arg + 1); + offset -= sizeof(void*) * calleeSavedRegisterCount; + reg = LocalsRegister; + } break; + default: + Q_UNIMPLEMENTED(); + } + return Pointer(reg, offset); +} + +template +void Assembler::copyValue(Result result, Source source) +{ +#ifdef VALUE_FITS_IN_REGISTER + // Use ReturnValueRegister as "scratch" register because loadArgument + // and storeArgument are functions that may need a scratch register themselves. + loadArgumentInRegister(source, ReturnValueRegister); + storeReturnValue(result); +#else + loadDouble(source, FPGpr0); + storeDouble(FPGpr0, result); +#endif +} + +template +void Assembler::copyValue(Result result, V4IR::Expr* source) +{ +#ifdef VALUE_FITS_IN_REGISTER + // Use ReturnValueRegister as "scratch" register because loadArgument + // and storeArgument are functions that may need a scratch register themselves. + loadArgumentInRegister(source, ReturnValueRegister); + storeReturnValue(result); +#else + if (V4IR::Temp *temp = source->asTemp()) { + loadDouble(temp, FPGpr0); + storeDouble(FPGpr0, result); + } else if (V4IR::Const *c = source->asConst()) { + QV4::Value v = convertToValue(c); + storeValue(v, result); + } else { + assert(! "not implemented"); + } +#endif +} + + +void Assembler::storeValue(QV4::Value value, V4IR::Temp* destination) +{ + Address addr = loadTempAddress(ScratchRegister, destination); + storeValue(value, addr); +} + +int Assembler::calculateStackFrameSize(int locals) +{ + const int stackSpaceAllocatedOtherwise = StackSpaceAllocatedUponFunctionEntry + + RegisterSize; // saved StackFrameRegister + + // space for the locals and the callee saved registers + int frameSize = locals * sizeof(QV4::Value) + sizeof(void*) * calleeSavedRegisterCount; + + frameSize = WTF::roundUpToMultipleOf(StackAlignment, frameSize + stackSpaceAllocatedOtherwise); + frameSize -= stackSpaceAllocatedOtherwise; + + return frameSize; +} + +void Assembler::enterStandardStackFrame(int locals) +{ + platformEnterStandardStackFrame(); + + // ### FIXME: Handle through calleeSavedRegisters mechanism + // or eliminate StackFrameRegister altogether. + push(StackFrameRegister); + move(StackPointerRegister, StackFrameRegister); + + int frameSize = calculateStackFrameSize(locals); + + subPtr(TrustedImm32(frameSize), StackPointerRegister); + + for (int i = 0; i < calleeSavedRegisterCount; ++i) + storePtr(calleeSavedRegisters[i], Address(StackFrameRegister, -(i + 1) * sizeof(void*))); + + move(StackFrameRegister, LocalsRegister); +} + +void Assembler::leaveStandardStackFrame(int locals) +{ + // restore the callee saved registers + for (int i = calleeSavedRegisterCount - 1; i >= 0; --i) + loadPtr(Address(StackFrameRegister, -(i + 1) * sizeof(void*)), calleeSavedRegisters[i]); + + int frameSize = calculateStackFrameSize(locals); + // Work around bug in ARMv7Assembler.h where add32(imm, sp, sp) doesn't + // work well for large immediates. +#if CPU(ARM_THUMB2) + move(TrustedImm32(frameSize), JSC::ARMRegisters::r3); + add32(JSC::ARMRegisters::r3, StackPointerRegister); +#else + addPtr(TrustedImm32(frameSize), StackPointerRegister); +#endif + + pop(StackFrameRegister); + platformLeaveStandardStackFrame(); +} + + + +#define OP(op) \ + { isel_stringIfy(op), op, 0, 0, 0 } +#define OPCONTEXT(op) \ + { isel_stringIfy(op), 0, op, 0, 0 } + +#define INLINE_OP(op, memOp, immOp) \ + { isel_stringIfy(op), op, 0, memOp, immOp } +#define INLINE_OPCONTEXT(op, memOp, immOp) \ + { isel_stringIfy(op), 0, op, memOp, immOp } + +#define NULL_OP \ + { 0, 0, 0, 0, 0 } + +const Assembler::BinaryOperationInfo Assembler::binaryOperations[QQmlJS::V4IR::LastAluOp + 1] = { + NULL_OP, // OpInvalid + NULL_OP, // OpIfTrue + NULL_OP, // OpNot + NULL_OP, // OpUMinus + NULL_OP, // OpUPlus + NULL_OP, // OpCompl + NULL_OP, // OpIncrement + NULL_OP, // OpDecrement + + INLINE_OP(__qmljs_bit_and, &Assembler::inline_and32, &Assembler::inline_and32), // OpBitAnd + INLINE_OP(__qmljs_bit_or, &Assembler::inline_or32, &Assembler::inline_or32), // OpBitOr + INLINE_OP(__qmljs_bit_xor, &Assembler::inline_xor32, &Assembler::inline_xor32), // OpBitXor + + INLINE_OPCONTEXT(__qmljs_add, &Assembler::inline_add32, &Assembler::inline_add32), // OpAdd + INLINE_OP(__qmljs_sub, &Assembler::inline_sub32, &Assembler::inline_sub32), // OpSub + INLINE_OP(__qmljs_mul, &Assembler::inline_mul32, &Assembler::inline_mul32), // OpMul + + OP(__qmljs_div), // OpDiv + OP(__qmljs_mod), // OpMod + + INLINE_OP(__qmljs_shl, &Assembler::inline_shl32, &Assembler::inline_shl32), // OpLShift + INLINE_OP(__qmljs_shr, &Assembler::inline_shr32, &Assembler::inline_shr32), // OpRShift + INLINE_OP(__qmljs_ushr, &Assembler::inline_ushr32, &Assembler::inline_ushr32), // OpURShift + + OP(__qmljs_gt), // OpGt + OP(__qmljs_lt), // OpLt + OP(__qmljs_ge), // OpGe + OP(__qmljs_le), // OpLe + OP(__qmljs_eq), // OpEqual + OP(__qmljs_ne), // OpNotEqual + OP(__qmljs_se), // OpStrictEqual + OP(__qmljs_sne), // OpStrictNotEqual + + OPCONTEXT(__qmljs_instanceof), // OpInstanceof + OPCONTEXT(__qmljs_in), // OpIn + + NULL_OP, // OpAnd + NULL_OP // OpOr +}; + +void Assembler::generateBinOp(V4IR::AluOp operation, V4IR::Temp* target, V4IR::Temp *left, V4IR::Temp *right) +{ + const BinaryOperationInfo& info = binaryOperations[operation]; + if (!info.fallbackImplementation && !info.contextImplementation) { + assert(!"unreachable"); + return; + } + + Value leftConst = Value::undefinedValue(); + Value rightConst = Value::undefinedValue(); + + bool canDoInline = info.inlineMemRegOp && info.inlineImmRegOp; + + if (canDoInline) { + if (left->asConst()) { + leftConst = convertToValue(left->asConst()); + canDoInline = canDoInline && leftConst.tryIntegerConversion(); + } + if (right->asConst()) { + rightConst = convertToValue(right->asConst()); + canDoInline = canDoInline && rightConst.tryIntegerConversion(); + } + } + + Jump binOpFinished; + + if (canDoInline) { + + Jump leftTypeCheck; + if (left->asTemp()) { + Address typeAddress = loadTempAddress(ScratchRegister, left->asTemp()); + typeAddress.offset += offsetof(QV4::Value, tag); + leftTypeCheck = branch32(NotEqual, typeAddress, TrustedImm32(QV4::Value::_Integer_Type)); + } + + Jump rightTypeCheck; + if (right->asTemp()) { + Address typeAddress = loadTempAddress(ScratchRegister, right->asTemp()); + typeAddress.offset += offsetof(QV4::Value, tag); + rightTypeCheck = branch32(NotEqual, typeAddress, TrustedImm32(QV4::Value::_Integer_Type)); + } + + if (left->asTemp()) { + Address leftValue = loadTempAddress(ScratchRegister, left->asTemp()); + leftValue.offset += offsetof(QV4::Value, int_32); + load32(leftValue, IntegerOpRegister); + } else { // left->asConst() + move(TrustedImm32(leftConst.integerValue()), IntegerOpRegister); + } + + Jump overflowCheck; + + if (right->asTemp()) { + Address rightValue = loadTempAddress(ScratchRegister, right->asTemp()); + rightValue.offset += offsetof(QV4::Value, int_32); + + overflowCheck = (this->*info.inlineMemRegOp)(rightValue, IntegerOpRegister); + } else { // right->asConst() + overflowCheck = (this->*info.inlineImmRegOp)(TrustedImm32(rightConst.integerValue()), IntegerOpRegister); + } + + Address resultAddr = loadTempAddress(ScratchRegister, target); + Address resultValueAddr = resultAddr; + resultValueAddr.offset += offsetof(QV4::Value, int_32); + store32(IntegerOpRegister, resultValueAddr); + + Address resultTypeAddr = resultAddr; + resultTypeAddr.offset += offsetof(QV4::Value, tag); + store32(TrustedImm32(QV4::Value::_Integer_Type), resultTypeAddr); + + binOpFinished = jump(); + + if (leftTypeCheck.isSet()) + leftTypeCheck.link(this); + if (rightTypeCheck.isSet()) + rightTypeCheck.link(this); + if (overflowCheck.isSet()) + overflowCheck.link(this); + } + + // Fallback + if (info.contextImplementation) + generateFunctionCallImp(Assembler::Void, info.name, info.contextImplementation, ContextRegister, + Assembler::PointerToValue(target), Assembler::Reference(left), Assembler::Reference(right)); + else + generateFunctionCallImp(Assembler::Void, info.name, info.fallbackImplementation, + Assembler::PointerToValue(target), Assembler::Reference(left), Assembler::Reference(right)); + + if (binOpFinished.isSet()) + binOpFinished.link(this); +} +#if OS(LINUX) || OS(MAC_OS_X) +static void printDisassembledOutputWithCalls(const char* output, const QHash& functions, + const QVector &identifiers) +{ + QByteArray processedOutput(output); + for (QHash::ConstIterator it = functions.begin(), end = functions.end(); + it != end; ++it) { + QByteArray ptrString = QByteArray::number(quintptr(it.key()), 16); + ptrString.prepend("0x"); + processedOutput = processedOutput.replace(ptrString, it.value()); + } + for (QVector::ConstIterator it = identifiers.begin(), end = identifiers.end(); + it != end; ++it) { + QByteArray ptrString = QByteArray::number(quintptr(*it), 16); + ptrString.prepend("0x"); + QByteArray replacement = "\"" + (*it)->toQString().toUtf8() + "\""; + processedOutput = processedOutput.replace(ptrString, replacement); + } + fprintf(stderr, "%s\n", processedOutput.constData()); +} +#endif + +void Assembler::recordLineNumber(int lineNumber) +{ + CodeLineNumerMapping mapping; + mapping.location = label(); + mapping.lineNumber = lineNumber; + codeLineNumberMappings << mapping; +} + + +void Assembler::link(QV4::Function *vmFunc) +{ + Label endOfCode = label(); +#if defined(Q_PROCESSOR_ARM) && !defined(Q_OS_IOS) + // Let the ARM exception table follow right after that + for (int i = 0, nops = UnwindHelper::unwindInfoSize() / 2; i < nops; ++i) + nop(); +#endif + + { + QHashIterator > it(_patches); + while (it.hasNext()) { + it.next(); + V4IR::BasicBlock *block = it.key(); + Label target = _addrs.value(block); + assert(target.isSet()); + foreach (Jump jump, it.value()) + jump.linkTo(target, this); + } + } + + JSC::JSGlobalData dummy(_engine->executableAllocator); + JSC::LinkBuffer linkBuffer(dummy, this, 0); + vmFunc->codeSize = linkBuffer.offsetOf(endOfCode); + + vmFunc->lineNumberMappings.resize(codeLineNumberMappings.count()); + for (int i = 0; i < codeLineNumberMappings.count(); ++i) { + QV4::LineNumberMapping mapping; + mapping.codeOffset = linkBuffer.offsetOf(codeLineNumberMappings.at(i).location); + mapping.lineNumber = codeLineNumberMappings.at(i).lineNumber; + vmFunc->lineNumberMappings[i] = mapping; + } + + QHash functions; + foreach (CallToLink ctl, _callsToLink) { + linkBuffer.link(ctl.call, ctl.externalFunction); + functions[ctl.externalFunction.value()] = ctl.functionName; + } + + foreach (const DataLabelPatch &p, _dataLabelPatches) + linkBuffer.patch(p.dataLabel, linkBuffer.locationOf(p.target)); + + { + QHashIterator > it(_labelPatches); + while (it.hasNext()) { + it.next(); + V4IR::BasicBlock *block = it.key(); + Label target = _addrs.value(block); + assert(target.isSet()); + foreach (DataLabelPtr label, it.value()) + linkBuffer.patch(label, linkBuffer.locationOf(target)); + } + } + +#if defined(Q_PROCESSOR_ARM) && !defined(Q_OS_IOS) + UnwindHelper::writeARMUnwindInfo(linkBuffer.debugAddress(), linkBuffer.offsetOf(endOfCode)); +#endif + + static bool showCode = !qgetenv("SHOW_CODE").isNull(); + if (showCode) { +#if OS(LINUX) && !defined(Q_OS_ANDROID) + char* disasmOutput = 0; + size_t disasmLength = 0; + FILE* disasmStream = open_memstream(&disasmOutput, &disasmLength); + WTF::setDataFile(disasmStream); +#elif OS(MAC_OS_X) + struct MemStream { + QByteArray buf; + static int write(void *cookie, const char *buf, int len) { + MemStream *stream = reinterpret_cast(cookie); + stream->buf.append(buf, len); + return len; + } + }; + MemStream memStream; + + FILE* disasmStream = fwopen(&memStream, MemStream::write); + WTF::setDataFile(disasmStream); +#endif + + QByteArray name = _function->name->toUtf8(); + if (name.isEmpty()) { + name = QByteArray::number(quintptr(_function), 16); + name.prepend("IR::Function(0x"); + name.append(")"); + } + vmFunc->codeRef = linkBuffer.finalizeCodeWithDisassembly("%s", name.data()); + + WTF::setDataFile(stderr); +#if (OS(LINUX) && !defined(Q_OS_ANDROID)) || OS(MAC_OS_X) + fclose(disasmStream); +# if OS(MAC_OS_X) + char *disasmOutput = memStream.buf.data(); +# endif +# if CPU(X86) || CPU(X86_64) + QHash idents; + printDisassembledOutputWithCalls(disasmOutput, functions, _vmFunction->identifiers); +# endif +# if OS(LINUX) + free(disasmOutput); +# endif +#endif + } else { + vmFunc->codeRef = linkBuffer.finalizeCodeWithoutDisassembly(); + } + + vmFunc->code = (Value (*)(QV4::ExecutionContext *, const uchar *)) vmFunc->codeRef.code().executableAddress(); +} + +InstructionSelection::InstructionSelection(QV4::ExecutionEngine *engine, V4IR::Module *module) + : EvalInstructionSelection(engine, module) + , _block(0) + , _function(0) + , _vmFunction(0) + , _as(0) + , _locals(0) +{ +} + +InstructionSelection::~InstructionSelection() +{ + delete _as; +} + +void InstructionSelection::run(QV4::Function *vmFunction, V4IR::Function *function) +{ + QVector lookups; + QSet reentryBlocks; + qSwap(_function, function); + qSwap(_vmFunction, vmFunction); + qSwap(_lookups, lookups); + qSwap(_reentryBlocks, reentryBlocks); + Assembler* oldAssembler = _as; + _as = new Assembler(_function, _vmFunction, engine()); + + V4IR::Optimizer opt(_function); + opt.run(); + if (opt.isInSSA()) { +#if CPU(X86_64) && (OS(MAC_OS_X) || OS(LINUX)) && 0 + // TODO: add a register allocator here. +#else + // No register allocator available for this platform, so: + opt.convertOutOfSSA(); + ConvertTemps().toStackSlots(_function); +#endif + } else { + ConvertTemps().toStackSlots(_function); + } + + int locals = (_function->tempCount + _function->maxNumberOfArguments) + 1; + locals = (locals + 1) & ~1; + qSwap(_locals, locals); + _as->enterStandardStackFrame(_locals); + + int contextPointer = 0; +#if !defined(RETURN_VALUE_IN_REGISTER) + // When the return VM value doesn't fit into a register, then + // the caller provides a pointer for storage as first argument. + // That shifts the index the context pointer argument by one. + contextPointer++; +#endif + +#ifdef ARGUMENTS_IN_REGISTERS + _as->move(_as->registerForArgument(contextPointer), Assembler::ContextRegister); +#else + _as->loadPtr(addressForArgument(contextPointer), Assembler::ContextRegister); +#endif + + for (int i = 0, ei = _function->basicBlocks.size(); i != ei; ++i) { + V4IR::BasicBlock *nextBlock = (i < ei - 1) ? _function->basicBlocks[i + 1] : 0; + _block = _function->basicBlocks[i]; + _as->registerBlock(_block, nextBlock); + + if (_reentryBlocks.contains(_block)) { + _as->enterStandardStackFrame(/*locals*/0); +#ifdef ARGUMENTS_IN_REGISTERS + _as->move(Assembler::registerForArgument(0), Assembler::ContextRegister); + _as->move(Assembler::registerForArgument(1), Assembler::LocalsRegister); +#else + _as->loadPtr(addressForArgument(0), Assembler::ContextRegister); + _as->loadPtr(addressForArgument(1), Assembler::LocalsRegister); +#endif + } + + foreach (V4IR::Stmt *s, _block->statements) { + if (s->location.isValid()) + _as->recordLineNumber(s->location.startLine); + s->accept(this); + } + } + + _as->link(_vmFunction); + + if (_lookups.size()) { + _vmFunction->lookups = new Lookup[_lookups.size()]; + memcpy(_vmFunction->lookups, _lookups.constData(), _lookups.size()*sizeof(Lookup)); + } + + UnwindHelper::registerFunction(_vmFunction); + + qSwap(_vmFunction, vmFunction); + qSwap(_function, function); + qSwap(_lookups, lookups); + qSwap(_reentryBlocks, reentryBlocks); + qSwap(_locals, locals); + delete _as; + _as = oldAssembler; +} + +void InstructionSelection::callBuiltinInvalid(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result) +{ + int argc = prepareVariableArguments(args); + QV4::String *s = identifier(*func->id); + + if (useFastLookups && func->global) { + uint index = addGlobalLookup(s); + generateFunctionCall(Assembler::Void, __qmljs_call_global_lookup, + Assembler::ContextRegister, Assembler::PointerToValue(result), + Assembler::TrustedImm32(index), + baseAddressForCallArguments(), + Assembler::TrustedImm32(argc)); + } else { + generateFunctionCall(Assembler::Void, __qmljs_call_activation_property, + Assembler::ContextRegister, Assembler::PointerToValue(result), + s, + baseAddressForCallArguments(), + Assembler::TrustedImm32(argc)); + } +} + +void InstructionSelection::callBuiltinTypeofMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_typeof_member, Assembler::ContextRegister, + Assembler::PointerToValue(result), Assembler::Reference(base), identifier(name)); +} + +void InstructionSelection::callBuiltinTypeofSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_typeof_element, + Assembler::ContextRegister, Assembler::PointerToValue(result), + Assembler::Reference(base), Assembler::Reference(index)); +} + +void InstructionSelection::callBuiltinTypeofName(const QString &name, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_typeof_name, Assembler::ContextRegister, Assembler::PointerToValue(result), identifier(name)); +} + +void InstructionSelection::callBuiltinTypeofValue(V4IR::Temp *value, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_typeof, Assembler::ContextRegister, + Assembler::PointerToValue(result), Assembler::Reference(value)); +} + +void InstructionSelection::callBuiltinDeleteMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_delete_member, Assembler::ContextRegister, + Assembler::PointerToValue(result), Assembler::Reference(base), identifier(name)); +} + +void InstructionSelection::callBuiltinDeleteSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_delete_subscript, Assembler::ContextRegister, + Assembler::PointerToValue(result), Assembler::Reference(base), Assembler::Reference(index)); +} + +void InstructionSelection::callBuiltinDeleteName(const QString &name, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_delete_name, Assembler::ContextRegister, Assembler::PointerToValue(result), identifier(name)); +} + +void InstructionSelection::callBuiltinDeleteValue(V4IR::Temp *result) +{ + _as->storeValue(Value::fromBoolean(false), result); +} + +void InstructionSelection::callBuiltinPostIncrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_post_increment_member, Assembler::ContextRegister, + Assembler::PointerToValue(result), Assembler::PointerToValue(base), identifier(name)); +} + +void InstructionSelection::callBuiltinPostIncrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_post_increment_element, Assembler::ContextRegister, + Assembler::PointerToValue(result), Assembler::Reference(base), Assembler::PointerToValue(index)); +} + +void InstructionSelection::callBuiltinPostIncrementName(const QString &name, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_post_increment_name, Assembler::ContextRegister, + Assembler::PointerToValue(result), identifier(name)); +} + +void InstructionSelection::callBuiltinPostIncrementValue(V4IR::Temp *value, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_post_increment, + Assembler::PointerToValue(result), Assembler::PointerToValue(value)); +} + +void InstructionSelection::callBuiltinPostDecrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_post_decrement_member, Assembler::ContextRegister, + Assembler::PointerToValue(result), Assembler::Reference(base), identifier(name)); +} + +void InstructionSelection::callBuiltinPostDecrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_post_decrement_element, Assembler::ContextRegister, + Assembler::PointerToValue(result), Assembler::Reference(base), + Assembler::Reference(index)); +} + +void InstructionSelection::callBuiltinPostDecrementName(const QString &name, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_post_decrement_name, Assembler::ContextRegister, + Assembler::PointerToValue(result), identifier(name)); +} + +void InstructionSelection::callBuiltinPostDecrementValue(V4IR::Temp *value, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_post_decrement, + Assembler::PointerToValue(result), Assembler::PointerToValue(value)); +} + +void InstructionSelection::callBuiltinThrow(V4IR::Temp *arg) +{ + generateFunctionCall(Assembler::Void, __qmljs_throw, Assembler::ContextRegister, Assembler::Reference(arg)); +} + +typedef void *(*MiddleOfFunctionEntryPoint(ExecutionContext *, void *localsPtr)); +static void *tryWrapper(ExecutionContext *context, void *localsPtr, MiddleOfFunctionEntryPoint tryBody, MiddleOfFunctionEntryPoint catchBody, + QV4::String *exceptionVarName, Value *exceptionVar) +{ + *exceptionVar = Value::undefinedValue(); + void *addressToContinueAt = 0; + try { + addressToContinueAt = tryBody(context, localsPtr); + } catch (Exception& ex) { + ex.accept(context); + *exceptionVar = ex.value(); + try { + ExecutionContext *catchContext = __qmljs_builtin_push_catch_scope(exceptionVarName, ex.value(), context); + addressToContinueAt = catchBody(catchContext, localsPtr); + context = __qmljs_builtin_pop_scope(catchContext); + } catch (Exception& ex) { + *exceptionVar = ex.value(); + ex.accept(context); + addressToContinueAt = catchBody(context, localsPtr); + } + } + return addressToContinueAt; +} + +void InstructionSelection::visitTry(V4IR::Try *t) +{ + // Call tryWrapper, which is going to re-enter the same function at the address of the try block. At then end + // of the try function the JIT code will return with the address of the sub-sequent instruction, which tryWrapper + // returns and to which we jump to. + + _reentryBlocks.insert(t->tryBlock); + _reentryBlocks.insert(t->catchBlock); + + generateFunctionCall(Assembler::ReturnValueRegister, tryWrapper, Assembler::ContextRegister, Assembler::LocalsRegister, + Assembler::ReentryBlock(t->tryBlock), Assembler::ReentryBlock(t->catchBlock), + identifier(t->exceptionVarName), Assembler::PointerToValue(t->exceptionVar)); + _as->jump(Assembler::ReturnValueRegister); +} + +void InstructionSelection::callBuiltinFinishTry() +{ + // This assumes that we're in code that was called by tryWrapper, so we return to try wrapper + // with the address that we'd like to continue at, which is right after the ret below. + Assembler::DataLabelPtr continuation = _as->moveWithPatch(Assembler::TrustedImmPtr(0), Assembler::ReturnValueRegister); + _as->leaveStandardStackFrame(/*locals*/0); + _as->ret(); + _as->addPatch(continuation, _as->label()); +} + +void InstructionSelection::callBuiltinForeachIteratorObject(V4IR::Temp *arg, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_foreach_iterator_object, Assembler::ContextRegister, Assembler::PointerToValue(result), Assembler::Reference(arg)); +} + +void InstructionSelection::callBuiltinForeachNextPropertyname(V4IR::Temp *arg, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_foreach_next_property_name, Assembler::PointerToValue(result), Assembler::Reference(arg)); +} + +void InstructionSelection::callBuiltinPushWithScope(V4IR::Temp *arg) +{ + generateFunctionCall(Assembler::ContextRegister, __qmljs_builtin_push_with_scope, Assembler::Reference(arg), Assembler::ContextRegister); +} + +void InstructionSelection::callBuiltinPopScope() +{ + generateFunctionCall(Assembler::ContextRegister, __qmljs_builtin_pop_scope, Assembler::ContextRegister); +} + +void InstructionSelection::callBuiltinDeclareVar(bool deletable, const QString &name) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_declare_var, Assembler::ContextRegister, + Assembler::TrustedImm32(deletable), identifier(name)); +} + +void InstructionSelection::callBuiltinDefineGetterSetter(V4IR::Temp *object, const QString &name, V4IR::Temp *getter, V4IR::Temp *setter) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_define_getter_setter, Assembler::ContextRegister, + Assembler::Reference(object), identifier(name), Assembler::PointerToValue(getter), Assembler::PointerToValue(setter)); +} + +void InstructionSelection::callBuiltinDefineProperty(V4IR::Temp *object, const QString &name, V4IR::Temp *value) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_define_property, Assembler::ContextRegister, + Assembler::Reference(object), identifier(name), Assembler::PointerToValue(value)); +} + +void InstructionSelection::callBuiltinDefineArray(V4IR::Temp *result, V4IR::ExprList *args) +{ + int length = prepareVariableArguments(args); + generateFunctionCall(Assembler::Void, __qmljs_builtin_define_array, Assembler::ContextRegister, + Assembler::PointerToValue(result), + baseAddressForCallArguments(), Assembler::TrustedImm32(length)); +} + +void InstructionSelection::callBuiltinDefineObjectLiteral(V4IR::Temp *result, V4IR::ExprList *args) +{ + int argc = 0; + + InternalClass *klass = engine()->emptyClass; + V4IR::ExprList *it = args; + while (it) { + V4IR::Name *name = it->expr->asName(); + it = it->next; + + bool isData = it->expr->asConst()->value; + it = it->next; + klass = klass->addMember(identifier(*name->id), isData ? QV4::Attr_Data : QV4::Attr_Accessor); + + _as->copyValue(argumentAddressForCall(argc++), it->expr); + + if (!isData) { + it = it->next; + _as->copyValue(argumentAddressForCall(argc++), it->expr); + } + + it = it->next; + } + + generateFunctionCall(Assembler::Void, __qmljs_builtin_define_object_literal, Assembler::ContextRegister, + Assembler::PointerToValue(result), baseAddressForCallArguments(), + Assembler::TrustedImmPtr(klass)); +} + +void InstructionSelection::callValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result) +{ + int argc = prepareVariableArguments(args); + V4IR::Temp* thisObject = 0; + generateFunctionCall(Assembler::Void, __qmljs_call_value, Assembler::ContextRegister, + Assembler::PointerToValue(result), Assembler::PointerToValue(thisObject), + Assembler::Reference(value), baseAddressForCallArguments(), Assembler::TrustedImm32(argc)); +} + +void InstructionSelection::loadThisObject(V4IR::Temp *temp) +{ +#if defined(VALUE_FITS_IN_REGISTER) + _as->load64(Pointer(Assembler::ContextRegister, offsetof(ExecutionContext, thisObject)), Assembler::ReturnValueRegister); + _as->storeReturnValue(temp); +#else + _as->copyValue(temp, Pointer(Assembler::ContextRegister, offsetof(ExecutionContext, thisObject))); +#endif +} + +void InstructionSelection::loadConst(V4IR::Const *sourceConst, V4IR::Temp *targetTemp) +{ + _as->storeValue(convertToValue(sourceConst), targetTemp); +} + +void InstructionSelection::loadString(const QString &str, V4IR::Temp *targetTemp) +{ + Value v = Value::fromString(identifier(str)); + _as->storeValue(v, targetTemp); +} + +void InstructionSelection::loadRegexp(V4IR::RegExp *sourceRegexp, V4IR::Temp *targetTemp) +{ + Value v = Value::fromObject(engine()->newRegExpObject(*sourceRegexp->value, + sourceRegexp->flags)); + _vmFunction->generatedValues.append(v); + _as->storeValue(v, targetTemp); +} + +void InstructionSelection::getActivationProperty(const V4IR::Name *name, V4IR::Temp *temp) +{ + String *propertyName = identifier(*name->id); + if (useFastLookups && name->global) { + uint index = addGlobalLookup(propertyName); + generateLookupCall(index, offsetof(QV4::Lookup, globalGetter), Assembler::ContextRegister, Assembler::PointerToValue(temp)); + return; + } + generateFunctionCall(Assembler::Void, __qmljs_get_activation_property, Assembler::ContextRegister, Assembler::PointerToValue(temp), propertyName); +} + +void InstructionSelection::setActivationProperty(V4IR::Temp *source, const QString &targetName) +{ + String *propertyName = identifier(targetName); + generateFunctionCall(Assembler::Void, __qmljs_set_activation_property, + Assembler::ContextRegister, propertyName, Assembler::Reference(source)); +} + +void InstructionSelection::initClosure(V4IR::Closure *closure, V4IR::Temp *target) +{ + QV4::Function *vmFunc = vmFunction(closure->value); + assert(vmFunc); + generateFunctionCall(Assembler::Void, __qmljs_init_closure, Assembler::ContextRegister, Assembler::PointerToValue(target), Assembler::TrustedImmPtr(vmFunc)); +} + +void InstructionSelection::getProperty(V4IR::Temp *base, const QString &name, V4IR::Temp *target) +{ + if (useFastLookups) { + QV4::String *s = identifier(name); + uint index = addLookup(s); + generateLookupCall(index, offsetof(QV4::Lookup, getter), Assembler::PointerToValue(target), + Assembler::Reference(base)); + } else { + generateFunctionCall(Assembler::Void, __qmljs_get_property, Assembler::ContextRegister, Assembler::PointerToValue(target), + Assembler::Reference(base), identifier(name)); + } +} + +void InstructionSelection::setProperty(V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName) +{ + if (useFastLookups) { + QV4::String *s = identifier(targetName); + uint index = addSetterLookup(s); + generateLookupCall(index, offsetof(QV4::Lookup, setter), Assembler::Reference(targetBase), Assembler::Reference(source)); + } else { + generateFunctionCall(Assembler::Void, __qmljs_set_property, Assembler::ContextRegister, + Assembler::Reference(targetBase), + identifier(targetName), Assembler::Reference(source)); + } +} + +void InstructionSelection::getElement(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *target) +{ + generateFunctionCall(Assembler::Void, __qmljs_get_element, Assembler::ContextRegister, + Assembler::PointerToValue(target), Assembler::Reference(base), + Assembler::Reference(index)); +} + +void InstructionSelection::setElement(V4IR::Temp *source, V4IR::Temp *targetBase, V4IR::Temp *targetIndex) +{ + generateFunctionCall(Assembler::Void, __qmljs_set_element, Assembler::ContextRegister, + Assembler::Reference(targetBase), Assembler::Reference(targetIndex), + Assembler::Reference(source)); +} + +void InstructionSelection::copyValue(V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp) +{ + _as->copyValue(targetTemp, sourceTemp); +} + +#define setOp(op, opName, operation) \ + do { op = operation; opName = isel_stringIfy(operation); } while (0) +#define setOpContext(op, opName, operation) \ + do { opContext = operation; opName = isel_stringIfy(operation); } while (0) + +void InstructionSelection::unop(V4IR::AluOp oper, V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp) +{ + UnaryOpName op = 0; + const char *opName = 0; + switch (oper) { + case V4IR::OpIfTrue: assert(!"unreachable"); break; + case V4IR::OpNot: setOp(op, opName, __qmljs_not); break; + case V4IR::OpUMinus: setOp(op, opName, __qmljs_uminus); break; + case V4IR::OpUPlus: setOp(op, opName, __qmljs_uplus); break; + case V4IR::OpCompl: setOp(op, opName, __qmljs_compl); break; + case V4IR::OpIncrement: setOp(op, opName, __qmljs_increment); break; + case V4IR::OpDecrement: setOp(op, opName, __qmljs_decrement); break; + default: assert(!"unreachable"); break; + } // switch + + if (op) + _as->generateFunctionCallImp(Assembler::Void, opName, op, Assembler::PointerToValue(targetTemp), + Assembler::Reference(sourceTemp)); +} + +void InstructionSelection::binop(V4IR::AluOp oper, V4IR::Expr *leftSource, V4IR::Expr *rightSource, V4IR::Temp *target) +{ + Q_ASSERT(leftSource->asTemp() && rightSource->asTemp()); + _as->generateBinOp(oper, target, leftSource->asTemp(), rightSource->asTemp()); +} + +void InstructionSelection::inplaceNameOp(V4IR::AluOp oper, V4IR::Temp *rightSource, const QString &targetName) +{ + InplaceBinOpName op = 0; + const char *opName = 0; + switch (oper) { + case V4IR::OpBitAnd: setOp(op, opName, __qmljs_inplace_bit_and_name); break; + case V4IR::OpBitOr: setOp(op, opName, __qmljs_inplace_bit_or_name); break; + case V4IR::OpBitXor: setOp(op, opName, __qmljs_inplace_bit_xor_name); break; + case V4IR::OpAdd: setOp(op, opName, __qmljs_inplace_add_name); break; + case V4IR::OpSub: setOp(op, opName, __qmljs_inplace_sub_name); break; + case V4IR::OpMul: setOp(op, opName, __qmljs_inplace_mul_name); break; + case V4IR::OpDiv: setOp(op, opName, __qmljs_inplace_div_name); break; + case V4IR::OpMod: setOp(op, opName, __qmljs_inplace_mod_name); break; + case V4IR::OpLShift: setOp(op, opName, __qmljs_inplace_shl_name); break; + case V4IR::OpRShift: setOp(op, opName, __qmljs_inplace_shr_name); break; + case V4IR::OpURShift: setOp(op, opName, __qmljs_inplace_ushr_name); break; + default: + Q_UNREACHABLE(); + break; + } + if (op) { + _as->generateFunctionCallImp(Assembler::Void, opName, op, Assembler::ContextRegister, + identifier(targetName), Assembler::Reference(rightSource)); + } +} + +void InstructionSelection::inplaceElementOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBaseTemp, V4IR::Temp *targetIndexTemp) +{ + InplaceBinOpElement op = 0; + const char *opName = 0; + switch (oper) { + case V4IR::OpBitAnd: setOp(op, opName, __qmljs_inplace_bit_and_element); break; + case V4IR::OpBitOr: setOp(op, opName, __qmljs_inplace_bit_or_element); break; + case V4IR::OpBitXor: setOp(op, opName, __qmljs_inplace_bit_xor_element); break; + case V4IR::OpAdd: setOp(op, opName, __qmljs_inplace_add_element); break; + case V4IR::OpSub: setOp(op, opName, __qmljs_inplace_sub_element); break; + case V4IR::OpMul: setOp(op, opName, __qmljs_inplace_mul_element); break; + case V4IR::OpDiv: setOp(op, opName, __qmljs_inplace_div_element); break; + case V4IR::OpMod: setOp(op, opName, __qmljs_inplace_mod_element); break; + case V4IR::OpLShift: setOp(op, opName, __qmljs_inplace_shl_element); break; + case V4IR::OpRShift: setOp(op, opName, __qmljs_inplace_shr_element); break; + case V4IR::OpURShift: setOp(op, opName, __qmljs_inplace_ushr_element); break; + default: + Q_UNREACHABLE(); + break; + } + + if (op) { + _as->generateFunctionCallImp(Assembler::Void, opName, op, Assembler::ContextRegister, + Assembler::Reference(targetBaseTemp), Assembler::Reference(targetIndexTemp), + Assembler::Reference(source)); + } +} + +void InstructionSelection::inplaceMemberOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName) +{ + InplaceBinOpMember op = 0; + const char *opName = 0; + switch (oper) { + case V4IR::OpBitAnd: setOp(op, opName, __qmljs_inplace_bit_and_member); break; + case V4IR::OpBitOr: setOp(op, opName, __qmljs_inplace_bit_or_member); break; + case V4IR::OpBitXor: setOp(op, opName, __qmljs_inplace_bit_xor_member); break; + case V4IR::OpAdd: setOp(op, opName, __qmljs_inplace_add_member); break; + case V4IR::OpSub: setOp(op, opName, __qmljs_inplace_sub_member); break; + case V4IR::OpMul: setOp(op, opName, __qmljs_inplace_mul_member); break; + case V4IR::OpDiv: setOp(op, opName, __qmljs_inplace_div_member); break; + case V4IR::OpMod: setOp(op, opName, __qmljs_inplace_mod_member); break; + case V4IR::OpLShift: setOp(op, opName, __qmljs_inplace_shl_member); break; + case V4IR::OpRShift: setOp(op, opName, __qmljs_inplace_shr_member); break; + case V4IR::OpURShift: setOp(op, opName, __qmljs_inplace_ushr_member); break; + default: + Q_UNREACHABLE(); + break; + } + + if (op) { + String* member = identifier(targetName); + _as->generateFunctionCallImp(Assembler::Void, opName, op, Assembler::ContextRegister, + Assembler::Reference(targetBase), identifier(targetName), + Assembler::Reference(source)); + } +} + +void InstructionSelection::callProperty(V4IR::Temp *base, const QString &name, + V4IR::ExprList *args, V4IR::Temp *result) +{ + assert(base != 0); + + int argc = prepareVariableArguments(args); + QV4::String *s = identifier(name); + + if (useFastLookups) { + uint index = addLookup(s); + generateFunctionCall(Assembler::Void, __qmljs_call_property_lookup, + Assembler::ContextRegister, Assembler::PointerToValue(result), + Assembler::Reference(base), Assembler::TrustedImm32(index), + baseAddressForCallArguments(), + Assembler::TrustedImm32(argc)); + } else { + generateFunctionCall(Assembler::Void, __qmljs_call_property, + Assembler::ContextRegister, Assembler::PointerToValue(result), + Assembler::Reference(base), s, + baseAddressForCallArguments(), + Assembler::TrustedImm32(argc)); + } +} + +void InstructionSelection::callSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::ExprList *args, V4IR::Temp *result) +{ + assert(base != 0); + + int argc = prepareVariableArguments(args); + generateFunctionCall(Assembler::Void, __qmljs_call_element, + Assembler::ContextRegister, Assembler::PointerToValue(result), + Assembler::Reference(base), Assembler::Reference(index), + baseAddressForCallArguments(), + Assembler::TrustedImm32(argc)); +} + +void InstructionSelection::convertType(V4IR::Temp *source, V4IR::Temp *target) +{ + // FIXME: do something more useful with this info + copyValue(source, target); +} + +String *InstructionSelection::identifier(const QString &s) +{ + String *str = engine()->newIdentifier(s); + _vmFunction->identifiers.append(str); + return str; +} + +void InstructionSelection::constructActivationProperty(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result) +{ + assert(func != 0); + + if (useFastLookups && func->global) { + int argc = prepareVariableArguments(args); + QV4::String *s = identifier(*func->id); + + uint index = addGlobalLookup(s); + generateFunctionCall(Assembler::Void, __qmljs_construct_global_lookup, + Assembler::ContextRegister, Assembler::PointerToValue(result), + Assembler::TrustedImm32(index), + baseAddressForCallArguments(), + Assembler::TrustedImm32(argc)); + return; + } + + callRuntimeMethod(result, __qmljs_construct_activation_property, func, args); +} + +void InstructionSelection::constructProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result) +{ + int argc = prepareVariableArguments(args); + generateFunctionCall(Assembler::Void, __qmljs_construct_property, Assembler::ContextRegister, + Assembler::PointerToValue(result), Assembler::Reference(base), identifier(name), baseAddressForCallArguments(), Assembler::TrustedImm32(argc)); +} + +void InstructionSelection::constructValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result) +{ + assert(value != 0); + + int argc = prepareVariableArguments(args); + generateFunctionCall(Assembler::Void, __qmljs_construct_value, Assembler::ContextRegister, + Assembler::PointerToValue(result), Assembler::Reference(value), baseAddressForCallArguments(), Assembler::TrustedImm32(argc)); +} + +void InstructionSelection::visitJump(V4IR::Jump *s) +{ + _as->jumpToBlock(_block, s->target); +} + +void InstructionSelection::visitCJump(V4IR::CJump *s) +{ + if (V4IR::Temp *t = s->cond->asTemp()) { + Address temp = _as->loadTempAddress(Assembler::ScratchRegister, t); + Address tag = temp; + tag.offset += offsetof(QV4::Value, tag); + Assembler::Jump booleanConversion = _as->branch32(Assembler::NotEqual, tag, Assembler::TrustedImm32(QV4::Value::Boolean_Type)); + + Address data = temp; + data.offset += offsetof(QV4::Value, int_32); + _as->load32(data, Assembler::ReturnValueRegister); + Assembler::Jump testBoolean = _as->jump(); + + booleanConversion.link(_as); + { + generateFunctionCall(Assembler::ReturnValueRegister, __qmljs_to_boolean, Assembler::Reference(t)); + } + + testBoolean.link(_as); + Assembler::Jump target = _as->branch32(Assembler::NotEqual, Assembler::ReturnValueRegister, Assembler::TrustedImm32(0)); + _as->addPatch(s->iftrue, target); + + _as->jumpToBlock(_block, s->iffalse); + return; + } else if (V4IR::Binop *b = s->cond->asBinop()) { + if (b->left->asTemp() && b->right->asTemp()) { + CmpOp op = 0; + CmpOpContext opContext = 0; + const char *opName = 0; + switch (b->op) { + default: Q_UNREACHABLE(); assert(!"todo"); break; + case V4IR::OpGt: setOp(op, opName, __qmljs_cmp_gt); break; + case V4IR::OpLt: setOp(op, opName, __qmljs_cmp_lt); break; + case V4IR::OpGe: setOp(op, opName, __qmljs_cmp_ge); break; + case V4IR::OpLe: setOp(op, opName, __qmljs_cmp_le); break; + case V4IR::OpEqual: setOp(op, opName, __qmljs_cmp_eq); break; + case V4IR::OpNotEqual: setOp(op, opName, __qmljs_cmp_ne); break; + case V4IR::OpStrictEqual: setOp(op, opName, __qmljs_cmp_se); break; + case V4IR::OpStrictNotEqual: setOp(op, opName, __qmljs_cmp_sne); break; + case V4IR::OpInstanceof: setOpContext(op, opName, __qmljs_cmp_instanceof); break; + case V4IR::OpIn: setOpContext(op, opName, __qmljs_cmp_in); break; + } // switch + + if (opContext) + _as->generateFunctionCallImp(Assembler::ReturnValueRegister, opName, opContext, Assembler::ContextRegister, + Assembler::Reference(b->left->asTemp()), + Assembler::Reference(b->right->asTemp())); + else + _as->generateFunctionCallImp(Assembler::ReturnValueRegister, opName, op, + Assembler::Reference(b->left->asTemp()), + Assembler::Reference(b->right->asTemp())); + + Assembler::Jump target = _as->branch32(Assembler::NotEqual, Assembler::ReturnValueRegister, Assembler::TrustedImm32(0)); + _as->addPatch(s->iftrue, target); + + _as->jumpToBlock(_block, s->iffalse); + return; + } else { + assert(!"wip"); + } + Q_UNIMPLEMENTED(); + } + Q_UNIMPLEMENTED(); + assert(!"TODO"); +} + +void InstructionSelection::visitRet(V4IR::Ret *s) +{ + if (V4IR::Temp *t = s->expr->asTemp()) { +#if defined(RETURN_VALUE_IN_REGISTER) +#if CPU(X86) + Address addr = _as->loadTempAddress(Assembler::ScratchRegister, t); + _as->load32(addr, JSC::X86Registers::eax); + addr.offset += 4; + _as->load32(addr, JSC::X86Registers::edx); +#else + _as->copyValue(Assembler::ReturnValueRegister, t); +#endif +#else + _as->loadPtr(addressForArgument(0), Assembler::ReturnValueRegister); + _as->copyValue(Address(Assembler::ReturnValueRegister, 0), t); +#endif + } else if (V4IR::Const *c = s->expr->asConst()) { + _as->copyValue(Assembler::ReturnValueRegister, c); + } else { + Q_UNIMPLEMENTED(); + Q_UNREACHABLE(); + Q_UNUSED(s); + } + + _as->leaveStandardStackFrame(_locals); +#if !defined(ARGUMENTS_IN_REGISTERS) && !defined(RETURN_VALUE_IN_REGISTER) + // Emulate ret(n) instruction + // Pop off return address into scratch register ... + _as->pop(Assembler::ScratchRegister); + // ... and overwrite the invisible argument with + // the return address. + _as->poke(Assembler::ScratchRegister); +#endif + _as->ret(); +} + +int InstructionSelection::prepareVariableArguments(V4IR::ExprList* args) +{ + int argc = 0; + for (V4IR::ExprList *it = args; it; it = it->next) { + ++argc; + } + + int i = 0; + for (V4IR::ExprList *it = args; it; it = it->next, ++i) { +// V4IR::Temp *arg = it->expr->asTemp(); +// assert(arg != 0); + _as->copyValue(argumentAddressForCall(i), it->expr); + } + + return argc; +} + +void InstructionSelection::callRuntimeMethodImp(V4IR::Temp *result, const char* name, ActivationMethod method, V4IR::Expr *base, V4IR::ExprList *args) +{ + V4IR::Name *baseName = base->asName(); + assert(baseName != 0); + + int argc = prepareVariableArguments(args); + _as->generateFunctionCallImp(Assembler::Void, name, method, Assembler::ContextRegister, Assembler::PointerToValue(result), + identifier(*baseName->id), baseAddressForCallArguments(), + Assembler::TrustedImm32(argc)); +} + + +uint InstructionSelection::addLookup(QV4::String *name) +{ + uint index = (uint)_lookups.size(); + QV4::Lookup l; + l.getter = Lookup::getterGeneric; + for (int i = 0; i < Lookup::Size; ++i) + l.classList[i] = 0; + l.level = -1; + l.index = UINT_MAX; + l.name = name; + _lookups.append(l); + return index; +} + +uint InstructionSelection::addSetterLookup(QV4::String *name) +{ + uint index = (uint)_lookups.size(); + QV4::Lookup l; + l.setter = Lookup::setterGeneric; + for (int i = 0; i < Lookup::Size; ++i) + l.classList[i] = 0; + l.level = -1; + l.index = UINT_MAX; + l.name = name; + _lookups.append(l); + return index; +} + +uint InstructionSelection::addGlobalLookup(QV4::String *name) +{ + uint index = (uint)_lookups.size(); + QV4::Lookup l; + l.globalGetter = Lookup::globalGetterGeneric; + for (int i = 0; i < Lookup::Size; ++i) + l.classList[i] = 0; + l.level = -1; + l.index = UINT_MAX; + l.name = name; + _lookups.append(l); + return index; +} diff --git a/src/qml/compiler/qv4isel_masm_p.h b/src/qml/compiler/qv4isel_masm_p.h new file mode 100644 index 0000000000..29fc8a5d67 --- /dev/null +++ b/src/qml/compiler/qv4isel_masm_p.h @@ -0,0 +1,939 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4ISEL_MASM_P_H +#define QV4ISEL_MASM_P_H + +#include "private/qv4global_p.h" +#include "qv4jsir_p.h" +#include "qv4isel_p.h" +#include "qv4isel_util_p.h" +#include "private/qv4object_p.h" +#include "private/qv4runtime_p.h" +#include "private/qv4lookup_p.h" + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { +namespace MASM { + +class Assembler : public JSC::MacroAssembler +{ +public: + Assembler(V4IR::Function* function, QV4::Function *vmFunction, QV4::ExecutionEngine *engine); +#if CPU(X86) + +#undef VALUE_FITS_IN_REGISTER +#undef ARGUMENTS_IN_REGISTERS + +#if OS(WINDOWS) + // Returned in EAX:EDX pair +#define RETURN_VALUE_IN_REGISTER +#else +#undef RETURN_VALUE_IN_REGISTER +#endif + +#define HAVE_ALU_OPS_WITH_MEM_OPERAND 1 + + static const RegisterID StackFrameRegister = JSC::X86Registers::ebp; + static const RegisterID StackPointerRegister = JSC::X86Registers::esp; + static const RegisterID LocalsRegister = JSC::X86Registers::edi; + static const RegisterID ContextRegister = JSC::X86Registers::esi; + static const RegisterID ReturnValueRegister = JSC::X86Registers::eax; + static const RegisterID ScratchRegister = JSC::X86Registers::ecx; + static const RegisterID IntegerOpRegister = JSC::X86Registers::eax; + static const FPRegisterID FPGpr0 = JSC::X86Registers::xmm0; + + static const int RegisterSize = 4; + + static const int RegisterArgumentCount = 0; + static RegisterID registerForArgument(int) + { + assert(false); + // Not reached. + return JSC::X86Registers::eax; + } + + // Return address is pushed onto stack by the CPU. + static const int StackSpaceAllocatedUponFunctionEntry = RegisterSize; + static const int StackShadowSpace = 0; + inline void platformEnterStandardStackFrame() {} + inline void platformLeaveStandardStackFrame() {} +#elif CPU(X86_64) + +#define VALUE_FITS_IN_REGISTER +#define ARGUMENTS_IN_REGISTERS +#define RETURN_VALUE_IN_REGISTER +#define HAVE_ALU_OPS_WITH_MEM_OPERAND 1 + + static const RegisterID StackFrameRegister = JSC::X86Registers::ebp; + static const RegisterID StackPointerRegister = JSC::X86Registers::esp; + static const RegisterID LocalsRegister = JSC::X86Registers::r12; + static const RegisterID ContextRegister = JSC::X86Registers::r14; + static const RegisterID ReturnValueRegister = JSC::X86Registers::eax; + static const RegisterID ScratchRegister = JSC::X86Registers::r10; + static const RegisterID IntegerOpRegister = JSC::X86Registers::eax; + static const FPRegisterID FPGpr0 = JSC::X86Registers::xmm0; + + static const int RegisterSize = 8; + +#if OS(WINDOWS) + static const int RegisterArgumentCount = 4; + static RegisterID registerForArgument(int index) + { + static RegisterID regs[RegisterArgumentCount] = { + JSC::X86Registers::ecx, + JSC::X86Registers::edx, + JSC::X86Registers::r8, + JSC::X86Registers::r9 + }; + assert(index >= 0 && index < RegisterArgumentCount); + return regs[index]; + }; + static const int StackShadowSpace = 32; +#else // Unix + static const int RegisterArgumentCount = 6; + static RegisterID registerForArgument(int index) + { + static RegisterID regs[RegisterArgumentCount] = { + JSC::X86Registers::edi, + JSC::X86Registers::esi, + JSC::X86Registers::edx, + JSC::X86Registers::ecx, + JSC::X86Registers::r8, + JSC::X86Registers::r9 + }; + assert(index >= 0 && index < RegisterArgumentCount); + return regs[index]; + }; + static const int StackShadowSpace = 0; +#endif + + // Return address is pushed onto stack by the CPU. + static const int StackSpaceAllocatedUponFunctionEntry = RegisterSize; + inline void platformEnterStandardStackFrame() {} + inline void platformLeaveStandardStackFrame() {} +#elif CPU(ARM) + +#undef VALUE_FITS_IN_REGISTER +#define ARGUMENTS_IN_REGISTERS +#undef RETURN_VALUE_IN_REGISTER +#undef HAVE_ALU_OPS_WITH_MEM_OPERAND + + static const RegisterID StackFrameRegister = JSC::ARMRegisters::r4; + static const RegisterID StackPointerRegister = JSC::ARMRegisters::sp; + static const RegisterID LocalsRegister = JSC::ARMRegisters::r7; + static const RegisterID ContextRegister = JSC::ARMRegisters::r5; + static const RegisterID ReturnValueRegister = JSC::ARMRegisters::r0; + static const RegisterID ScratchRegister = JSC::ARMRegisters::r6; + static const RegisterID IntegerOpRegister = JSC::ARMRegisters::r0; + static const FPRegisterID FPGpr0 = JSC::ARMRegisters::d0; + + static const int RegisterSize = 4; + + static const RegisterID RegisterArgument1 = JSC::ARMRegisters::r0; + static const RegisterID RegisterArgument2 = JSC::ARMRegisters::r1; + static const RegisterID RegisterArgument3 = JSC::ARMRegisters::r2; + static const RegisterID RegisterArgument4 = JSC::ARMRegisters::r3; + + static const int RegisterArgumentCount = 4; + static RegisterID registerForArgument(int index) + { + assert(index >= 0 && index < RegisterArgumentCount); + return static_cast(JSC::ARMRegisters::r0 + index); + }; + + // Registers saved in platformEnterStandardStackFrame below. + static const int StackSpaceAllocatedUponFunctionEntry = 5 * RegisterSize; + static const int StackShadowSpace = 0; + inline void platformEnterStandardStackFrame() + { + // Move the register arguments onto the stack as if they were + // pushed by the caller, just like on ia32. This gives us consistent + // access to the parameters if we need to. + push(JSC::ARMRegisters::r3); + push(JSC::ARMRegisters::r2); + push(JSC::ARMRegisters::r1); + push(JSC::ARMRegisters::r0); + push(JSC::ARMRegisters::lr); + } + inline void platformLeaveStandardStackFrame() + { + pop(JSC::ARMRegisters::lr); + addPtr(TrustedImm32(4 * RegisterSize), StackPointerRegister); + } +#else +#error The JIT needs to be ported to this platform. +#endif + static const int calleeSavedRegisterCount; + +#if CPU(X86) || CPU(X86_64) + static const int StackAlignment = 16; +#elif CPU(ARM) + // Per AAPCS + static const int StackAlignment = 8; +#else +#error Stack alignment unknown for this platform. +#endif + + // Explicit type to allow distinguishing between + // pushing an address itself or the value it points + // to onto the stack when calling functions. + struct Pointer : public Address + { + explicit Pointer(const Address& addr) + : Address(addr) + {} + explicit Pointer(RegisterID reg, int32_t offset) + : Address(reg, offset) + {} + }; + + struct VoidType { VoidType() {} }; + static const VoidType Void; + + + typedef JSC::FunctionPtr FunctionPtr; + + struct CallToLink { + Call call; + FunctionPtr externalFunction; + const char* functionName; + }; + struct PointerToValue { + PointerToValue(V4IR::Temp *value) : value(value) {} + V4IR::Temp *value; + }; + struct Reference { + Reference(V4IR::Temp *value) : value(value) {} + V4IR::Temp *value; + }; + + struct ReentryBlock { + ReentryBlock(V4IR::BasicBlock *b) : block(b) {} + V4IR::BasicBlock *block; + }; + + void callAbsolute(const char* functionName, FunctionPtr function) { + CallToLink ctl; + ctl.call = call(); + ctl.externalFunction = function; + ctl.functionName = functionName; + _callsToLink.append(ctl); + } + + void callAbsolute(const char* /*functionName*/, Address addr) { + call(addr); + } + + void registerBlock(V4IR::BasicBlock*, V4IR::BasicBlock *nextBlock); + void jumpToBlock(V4IR::BasicBlock* current, V4IR::BasicBlock *target); + void addPatch(V4IR::BasicBlock* targetBlock, Jump targetJump); + void addPatch(DataLabelPtr patch, Label target); + void addPatch(DataLabelPtr patch, V4IR::BasicBlock *target); + + Pointer loadTempAddress(RegisterID reg, V4IR::Temp *t); + + void loadArgumentInRegister(RegisterID source, RegisterID dest) + { + move(source, dest); + } + + void loadArgumentInRegister(TrustedImmPtr ptr, RegisterID dest) + { + move(TrustedImmPtr(ptr), dest); + } + + void loadArgumentInRegister(const Pointer& ptr, RegisterID dest) + { + addPtr(TrustedImm32(ptr.offset), ptr.base, dest); + } + + void loadArgumentInRegister(PointerToValue temp, RegisterID dest) + { + if (!temp.value) { + loadArgumentInRegister(TrustedImmPtr(0), dest); + } else { + Pointer addr = loadTempAddress(dest, temp.value); + loadArgumentInRegister(addr, dest); + } + } + + void loadArgumentInRegister(Reference temp, RegisterID dest) + { + assert(temp.value); + Pointer addr = loadTempAddress(dest, temp.value); + loadArgumentInRegister(addr, dest); + } + + void loadArgumentInRegister(ReentryBlock block, RegisterID dest) + { + assert(block.block); + DataLabelPtr patch = moveWithPatch(TrustedImmPtr(0), dest); + addPatch(patch, block.block); + } + +#ifdef VALUE_FITS_IN_REGISTER + void loadArgumentInRegister(V4IR::Temp* temp, RegisterID dest) + { + if (!temp) { + QV4::Value undefined = QV4::Value::undefinedValue(); + move(TrustedImm64(undefined.val), dest); + } else { + Pointer addr = loadTempAddress(dest, temp); + load64(addr, dest); + } + } + + void loadArgumentInRegister(V4IR::Const* c, RegisterID dest) + { + QV4::Value v = convertToValue(c); + move(TrustedImm64(v.val), dest); + } + + void loadArgumentInRegister(V4IR::Expr* expr, RegisterID dest) + { + if (!expr) { + QV4::Value undefined = QV4::Value::undefinedValue(); + move(TrustedImm64(undefined.val), dest); + } else if (expr->asTemp()){ + loadArgumentInRegister(expr->asTemp(), dest); + } else if (expr->asConst()) { + loadArgumentInRegister(expr->asConst(), dest); + } else { + assert(!"unimplemented expression type in loadArgument"); + } + } +#else + void loadArgumentInRegister(V4IR::Expr*, RegisterID) + { + assert(!"unimplemented: expression in loadArgument"); + } +#endif + + void loadArgumentInRegister(QV4::String* string, RegisterID dest) + { + loadArgumentInRegister(TrustedImmPtr(string), dest); + } + + void loadArgumentInRegister(TrustedImm32 imm32, RegisterID dest) + { + xorPtr(dest, dest); + if (imm32.m_value) + move(imm32, dest); + } + + void storeReturnValue(RegisterID dest) + { + move(ReturnValueRegister, dest); + } + +#ifdef VALUE_FITS_IN_REGISTER + void storeReturnValue(const Pointer &dest) + { + store64(ReturnValueRegister, dest); + } + + void storeReturnValue(V4IR::Temp *temp) + { + if (!temp) + return; + Pointer addr = loadTempAddress(ScratchRegister, temp); + storeReturnValue(addr); + } +#endif + + void storeReturnValue(VoidType) + { + } + + template + void loadArgumentOnStack(RegisterID reg) + { + poke(reg, StackSlot); + } + + template + void loadArgumentOnStack(TrustedImm32 value) + { + poke(value, StackSlot); + } + + template + void loadArgumentOnStack(const Pointer& ptr) + { + addPtr(TrustedImm32(ptr.offset), ptr.base, ScratchRegister); + poke(ScratchRegister, StackSlot); + } + + template + void loadArgumentOnStack(PointerToValue temp) + { + if (temp.value) { + Pointer ptr = loadTempAddress(ScratchRegister, temp.value); + loadArgumentOnStack(ptr); + } else { + poke(TrustedImmPtr(0), StackSlot); + } + } + + template + void loadArgumentOnStack(Reference temp) + { + assert (temp.value); + + Pointer ptr = loadTempAddress(ScratchRegister, temp.value); + loadArgumentOnStack(ptr); + } + + template + void loadArgumentOnStack(ReentryBlock block) + { + assert(block.block); + DataLabelPtr patch = moveWithPatch(TrustedImmPtr(0), ScratchRegister); + poke(ScratchRegister, StackSlot); + addPatch(patch, block.block); + } + + template + void loadArgumentOnStack(TrustedImmPtr ptr) + { + move(TrustedImmPtr(ptr), ScratchRegister); + poke(ScratchRegister, StackSlot); + } + + template + void loadArgumentOnStack(QV4::String* name) + { + poke(TrustedImmPtr(name), StackSlot); + } + + using JSC::MacroAssembler::loadDouble; + void loadDouble(V4IR::Temp* temp, FPRegisterID dest) + { + Pointer ptr = loadTempAddress(ScratchRegister, temp); + loadDouble(ptr, dest); + } + + using JSC::MacroAssembler::storeDouble; + void storeDouble(FPRegisterID source, V4IR::Temp* temp) + { + Pointer ptr = loadTempAddress(ScratchRegister, temp); + storeDouble(source, ptr); + } + + template + void copyValue(Result result, Source source); + template + void copyValue(Result result, V4IR::Expr* source); + + void storeValue(QV4::Value value, Address destination) + { +#ifdef VALUE_FITS_IN_REGISTER + store64(TrustedImm64(value.val), destination); +#else + store32(TrustedImm32(value.int_32), destination); + destination.offset += 4; + store32(TrustedImm32(value.tag), destination); +#endif + } + + void storeValue(QV4::Value value, V4IR::Temp* temp); + + static int calculateStackFrameSize(int locals); + void enterStandardStackFrame(int locals); + void leaveStandardStackFrame(int locals); + + template + void loadArgumentOnStackOrRegister(const T &value) + { + if (argumentNumber < RegisterArgumentCount) + loadArgumentInRegister(value, registerForArgument(argumentNumber)); + else +#if OS(WINDOWS) && CPU(X86_64) + loadArgumentOnStack(value); +#else // Sanity: + loadArgumentOnStack(value); +#endif + } + + template + void loadArgumentOnStackOrRegister(const VoidType &value) + { + Q_UNUSED(value); + } + + template + struct Select + { + enum { Chosen = First }; + }; + + template + struct Select + { + enum { Chosen = Second }; + }; + + template + struct SizeOnStack + { + enum { Size = Select= RegisterArgumentCount, QT_POINTER_SIZE, 0>::Chosen }; + }; + + template + struct SizeOnStack + { + enum { Size = 0 }; + }; + + + template + void generateFunctionCallImp(ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5, Arg6 arg6) + { + int stackSpaceNeeded = SizeOnStack<0, Arg1>::Size + + SizeOnStack<1, Arg2>::Size + + SizeOnStack<2, Arg3>::Size + + SizeOnStack<3, Arg4>::Size + + SizeOnStack<4, Arg5>::Size + + SizeOnStack<5, Arg6>::Size + + StackShadowSpace; + + if (stackSpaceNeeded) { + stackSpaceNeeded = WTF::roundUpToMultipleOf(StackAlignment, stackSpaceNeeded); + sub32(TrustedImm32(stackSpaceNeeded), StackPointerRegister); + } + + loadArgumentOnStackOrRegister<5>(arg6); + loadArgumentOnStackOrRegister<4>(arg5); + loadArgumentOnStackOrRegister<3>(arg4); + loadArgumentOnStackOrRegister<2>(arg3); + loadArgumentOnStackOrRegister<1>(arg2); + loadArgumentOnStackOrRegister<0>(arg1); + + callAbsolute(functionName, function); + + storeReturnValue(r); + + if (stackSpaceNeeded) + add32(TrustedImm32(stackSpaceNeeded), StackPointerRegister); + } + + template + void generateFunctionCallImp(ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5) + { + generateFunctionCallImp(r, functionName, function, arg1, arg2, arg3, arg4, arg5, VoidType()); + } + + template + void generateFunctionCallImp(ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4) + { + generateFunctionCallImp(r, functionName, function, arg1, arg2, arg3, arg4, VoidType()); + } + + template + void generateFunctionCallImp(ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2, Arg3 arg3) + { + generateFunctionCallImp(r, functionName, function, arg1, arg2, arg3, VoidType(), VoidType()); + } + + template + void generateFunctionCallImp(ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2) + { + generateFunctionCallImp(r, functionName, function, arg1, arg2, VoidType(), VoidType(), VoidType()); + } + + template + void generateFunctionCallImp(ArgRet r, const char* functionName, Callable function, Arg1 arg1) + { + generateFunctionCallImp(r, functionName, function, arg1, VoidType(), VoidType(), VoidType(), VoidType()); + } + + typedef Jump (Assembler::*MemRegBinOp)(Address, RegisterID); + typedef Jump (Assembler::*ImmRegBinOp)(TrustedImm32, RegisterID); + + struct BinaryOperationInfo { + const char *name; + QV4::BinOp fallbackImplementation; + QV4::BinOpContext contextImplementation; + MemRegBinOp inlineMemRegOp; + ImmRegBinOp inlineImmRegOp; + }; + + static const BinaryOperationInfo binaryOperations[QQmlJS::V4IR::LastAluOp + 1]; + + void generateBinOp(V4IR::AluOp operation, V4IR::Temp* target, V4IR::Temp* left, V4IR::Temp* right); + + Jump inline_add32(Address addr, RegisterID reg) + { +#if HAVE(ALU_OPS_WITH_MEM_OPERAND) + return branchAdd32(Overflow, addr, reg); +#else + load32(addr, ScratchRegister); + return branchAdd32(Overflow, ScratchRegister, reg); +#endif + } + + Jump inline_add32(TrustedImm32 imm, RegisterID reg) + { + return branchAdd32(Overflow, imm, reg); + } + + Jump inline_sub32(Address addr, RegisterID reg) + { +#if HAVE(ALU_OPS_WITH_MEM_OPERAND) + return branchSub32(Overflow, addr, reg); +#else + load32(addr, ScratchRegister); + return branchSub32(Overflow, ScratchRegister, reg); +#endif + } + + Jump inline_sub32(TrustedImm32 imm, RegisterID reg) + { + return branchSub32(Overflow, imm, reg); + } + + Jump inline_mul32(Address addr, RegisterID reg) + { +#if HAVE(ALU_OPS_WITH_MEM_OPERAND) + return branchMul32(Overflow, addr, reg); +#else + load32(addr, ScratchRegister); + return branchMul32(Overflow, ScratchRegister, reg); +#endif + } + + Jump inline_mul32(TrustedImm32 imm, RegisterID reg) + { + return branchMul32(Overflow, imm, reg, reg); + } + + Jump inline_shl32(Address addr, RegisterID reg) + { + load32(addr, ScratchRegister); + and32(TrustedImm32(0x1f), ScratchRegister); + lshift32(ScratchRegister, reg); + return Jump(); + } + + Jump inline_shl32(TrustedImm32 imm, RegisterID reg) + { + imm.m_value &= 0x1f; + lshift32(imm, reg); + return Jump(); + } + + Jump inline_shr32(Address addr, RegisterID reg) + { + load32(addr, ScratchRegister); + and32(TrustedImm32(0x1f), ScratchRegister); + rshift32(ScratchRegister, reg); + return Jump(); + } + + Jump inline_shr32(TrustedImm32 imm, RegisterID reg) + { + imm.m_value &= 0x1f; + rshift32(imm, reg); + return Jump(); + } + + Jump inline_ushr32(Address addr, RegisterID reg) + { + load32(addr, ScratchRegister); + and32(TrustedImm32(0x1f), ScratchRegister); + urshift32(ScratchRegister, reg); + return branchTest32(Signed, reg, reg); + } + + Jump inline_ushr32(TrustedImm32 imm, RegisterID reg) + { + imm.m_value &= 0x1f; + urshift32(imm, reg); + return branchTest32(Signed, reg, reg); + } + + Jump inline_and32(Address addr, RegisterID reg) + { +#if HAVE(ALU_OPS_WITH_MEM_OPERAND) + and32(addr, reg); +#else + load32(addr, ScratchRegister); + and32(ScratchRegister, reg); +#endif + return Jump(); + } + + Jump inline_and32(TrustedImm32 imm, RegisterID reg) + { + and32(imm, reg); + return Jump(); + } + + Jump inline_or32(Address addr, RegisterID reg) + { +#if HAVE(ALU_OPS_WITH_MEM_OPERAND) + or32(addr, reg); +#else + load32(addr, ScratchRegister); + or32(ScratchRegister, reg); +#endif + return Jump(); + } + + Jump inline_or32(TrustedImm32 imm, RegisterID reg) + { + or32(imm, reg); + return Jump(); + } + + Jump inline_xor32(Address addr, RegisterID reg) + { +#if HAVE(ALU_OPS_WITH_MEM_OPERAND) + xor32(addr, reg); +#else + load32(addr, ScratchRegister); + xor32(ScratchRegister, reg); +#endif + return Jump(); + } + + Jump inline_xor32(TrustedImm32 imm, RegisterID reg) + { + xor32(imm, reg); + return Jump(); + } + + void link(QV4::Function *vmFunc); + + void recordLineNumber(int lineNumber); + +private: + V4IR::Function *_function; + QV4::Function *_vmFunction; + QHash _addrs; + QHash > _patches; + QList _callsToLink; + + struct DataLabelPatch { + DataLabelPtr dataLabel; + Label target; + }; + QList _dataLabelPatches; + + QHash > _labelPatches; + V4IR::BasicBlock *_nextBlock; + + QV4::ExecutionEngine *_engine; + + struct CodeLineNumerMapping + { + Assembler::Label location; + int lineNumber; + }; + QVector codeLineNumberMappings; +}; + +class Q_QML_EXPORT InstructionSelection: + protected V4IR::IRDecoder, + public EvalInstructionSelection +{ +public: + InstructionSelection(QV4::ExecutionEngine *engine, V4IR::Module *module); + ~InstructionSelection(); + + virtual void run(QV4::Function *vmFunction, V4IR::Function *function); + +protected: + virtual void callBuiltinInvalid(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result); + virtual void callBuiltinTypeofMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result); + virtual void callBuiltinTypeofSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result); + virtual void callBuiltinTypeofName(const QString &name, V4IR::Temp *result); + virtual void callBuiltinTypeofValue(V4IR::Temp *value, V4IR::Temp *result); + virtual void callBuiltinDeleteMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result); + virtual void callBuiltinDeleteSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result); + virtual void callBuiltinDeleteName(const QString &name, V4IR::Temp *result); + virtual void callBuiltinDeleteValue(V4IR::Temp *result); + virtual void callBuiltinPostDecrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result); + virtual void callBuiltinPostDecrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result); + virtual void callBuiltinPostDecrementName(const QString &name, V4IR::Temp *result); + virtual void callBuiltinPostDecrementValue(V4IR::Temp *value, V4IR::Temp *result); + virtual void callBuiltinPostIncrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result); + virtual void callBuiltinPostIncrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result); + virtual void callBuiltinPostIncrementName(const QString &name, V4IR::Temp *result); + virtual void callBuiltinPostIncrementValue(V4IR::Temp *value, V4IR::Temp *result); + virtual void callBuiltinThrow(V4IR::Temp *arg); + virtual void callBuiltinFinishTry(); + virtual void callBuiltinForeachIteratorObject(V4IR::Temp *arg, V4IR::Temp *result); + virtual void callBuiltinForeachNextPropertyname(V4IR::Temp *arg, V4IR::Temp *result); + virtual void callBuiltinPushWithScope(V4IR::Temp *arg); + virtual void callBuiltinPopScope(); + virtual void callBuiltinDeclareVar(bool deletable, const QString &name); + virtual void callBuiltinDefineGetterSetter(V4IR::Temp *object, const QString &name, V4IR::Temp *getter, V4IR::Temp *setter); + virtual void callBuiltinDefineProperty(V4IR::Temp *object, const QString &name, V4IR::Temp *value); + virtual void callBuiltinDefineArray(V4IR::Temp *result, V4IR::ExprList *args); + virtual void callBuiltinDefineObjectLiteral(V4IR::Temp *result, V4IR::ExprList *args); + virtual void callValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result); + virtual void callProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result); + virtual void callSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::ExprList *args, V4IR::Temp *result); + virtual void convertType(V4IR::Temp *source, V4IR::Temp *target); + virtual void loadThisObject(V4IR::Temp *temp); + virtual void loadConst(V4IR::Const *sourceConst, V4IR::Temp *targetTemp); + virtual void loadString(const QString &str, V4IR::Temp *targetTemp); + virtual void loadRegexp(V4IR::RegExp *sourceRegexp, V4IR::Temp *targetTemp); + virtual void getActivationProperty(const V4IR::Name *name, V4IR::Temp *temp); + virtual void setActivationProperty(V4IR::Temp *source, const QString &targetName); + virtual void initClosure(V4IR::Closure *closure, V4IR::Temp *target); + virtual void getProperty(V4IR::Temp *base, const QString &name, V4IR::Temp *target); + virtual void setProperty(V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName); + virtual void getElement(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *target); + virtual void setElement(V4IR::Temp *source, V4IR::Temp *targetBase, V4IR::Temp *targetIndex); + virtual void copyValue(V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp); + virtual void unop(V4IR::AluOp oper, V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp); + virtual void binop(V4IR::AluOp oper, V4IR::Expr *leftSource, V4IR::Expr *rightSource, V4IR::Temp *target); + virtual void inplaceNameOp(V4IR::AluOp oper, V4IR::Temp *rightSource, const QString &targetName); + virtual void inplaceElementOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBaseTemp, V4IR::Temp *targetIndexTemp); + virtual void inplaceMemberOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName); + + typedef Assembler::Address Address; + typedef Assembler::Pointer Pointer; + + Address addressForArgument(int index) const + { + // StackFrameRegister points to its old value on the stack, and above + // it we have the return address, hence the need to step over two + // values before reaching the first argument. + return Address(Assembler::StackFrameRegister, (index + 2) * sizeof(void*)); + } + + // Some run-time functions take (Value* args, int argc). This function is for populating + // the args. + Pointer argumentAddressForCall(int argument) + { + const int index = _function->maxNumberOfArguments - argument; + return Pointer(Assembler::LocalsRegister, sizeof(QV4::Value) * (-index) + - sizeof(void*) // size of ebp + - sizeof(void*) * Assembler::calleeSavedRegisterCount + ); + } + Pointer baseAddressForCallArguments() + { + return argumentAddressForCall(0); + } + + QV4::String *identifier(const QString &s); + virtual void constructActivationProperty(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result); + virtual void constructProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result); + virtual void constructValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result); + + virtual void visitJump(V4IR::Jump *); + virtual void visitCJump(V4IR::CJump *); + virtual void visitRet(V4IR::Ret *); + virtual void visitTry(V4IR::Try *); + +private: + #define isel_stringIfyx(s) #s + #define isel_stringIfy(s) isel_stringIfyx(s) + + #define generateFunctionCall(t, function, ...) \ + _as->generateFunctionCallImp(t, isel_stringIfy(function), function, __VA_ARGS__) + + int prepareVariableArguments(V4IR::ExprList* args); + + typedef void (*ActivationMethod)(QV4::ExecutionContext *, QV4::Value *result, QV4::String *name, QV4::Value *args, int argc); + void callRuntimeMethodImp(V4IR::Temp *result, const char* name, ActivationMethod method, V4IR::Expr *base, V4IR::ExprList *args); +#define callRuntimeMethod(result, function, ...) \ + callRuntimeMethodImp(result, isel_stringIfy(function), function, __VA_ARGS__) + + uint addLookup(QV4::String *name); + uint addSetterLookup(QV4::String *name); + uint addGlobalLookup(QV4::String *name); + + template + void generateLookupCall(uint index, uint getterSetterOffset, Arg1 arg1, Arg2 arg2) + { + _as->loadPtr(Assembler::Address(Assembler::ContextRegister, offsetof(QV4::ExecutionContext, lookups)), + Assembler::ReturnValueRegister); + + Assembler::Pointer lookupAddr(Assembler::ReturnValueRegister, index * sizeof(QV4::Lookup)); + + Assembler::Address getterSetter = lookupAddr; + getterSetter.offset += getterSetterOffset; + + _as->generateFunctionCallImp(Assembler::Void, "lookup getter/setter", getterSetter, lookupAddr, arg1, arg2); + } + + template + void generateLookupCall(uint index, uint getterSetterOffset, Arg1 arg1) + { + generateLookupCall(index, getterSetterOffset, arg1, Assembler::VoidType()); + } + + V4IR::BasicBlock *_block; + V4IR::Function* _function; + QV4::Function* _vmFunction; + QVector _lookups; + Assembler* _as; + QSet _reentryBlocks; + int _locals; +}; + +class Q_QML_EXPORT ISelFactory: public EvalISelFactory +{ +public: + virtual ~ISelFactory() {} + virtual EvalInstructionSelection *create(QV4::ExecutionEngine *engine, V4IR::Module *module) + { return new InstructionSelection(engine, module); } + virtual bool jitCompileRegexps() const + { return true; } +}; + +} // end of namespace MASM +} // end of namespace QQmlJS + +QT_END_NAMESPACE + +#endif // QV4ISEL_MASM_P_H diff --git a/src/qml/compiler/qv4isel_moth.cpp b/src/qml/compiler/qv4isel_moth.cpp new file mode 100644 index 0000000000..610f429f5d --- /dev/null +++ b/src/qml/compiler/qv4isel_moth.cpp @@ -0,0 +1,1089 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4isel_util_p.h" +#include "qv4isel_moth_p.h" +#include "qv4vme_moth_p.h" +#include "qv4ssa_p.h" +#include +#include +#include +#include + +#undef USE_TYPE_INFO + +using namespace QQmlJS; +using namespace QQmlJS::Moth; + +namespace { + +inline QV4::BinOp aluOpFunction(V4IR::AluOp op) +{ + switch (op) { + case V4IR::OpInvalid: + return 0; + case V4IR::OpIfTrue: + return 0; + case V4IR::OpNot: + return 0; + case V4IR::OpUMinus: + return 0; + case V4IR::OpUPlus: + return 0; + case V4IR::OpCompl: + return 0; + case V4IR::OpBitAnd: + return QV4::__qmljs_bit_and; + case V4IR::OpBitOr: + return QV4::__qmljs_bit_or; + case V4IR::OpBitXor: + return QV4::__qmljs_bit_xor; + case V4IR::OpAdd: + return 0; + case V4IR::OpSub: + return QV4::__qmljs_sub; + case V4IR::OpMul: + return QV4::__qmljs_mul; + case V4IR::OpDiv: + return QV4::__qmljs_div; + case V4IR::OpMod: + return QV4::__qmljs_mod; + case V4IR::OpLShift: + return QV4::__qmljs_shl; + case V4IR::OpRShift: + return QV4::__qmljs_shr; + case V4IR::OpURShift: + return QV4::__qmljs_ushr; + case V4IR::OpGt: + return QV4::__qmljs_gt; + case V4IR::OpLt: + return QV4::__qmljs_lt; + case V4IR::OpGe: + return QV4::__qmljs_ge; + case V4IR::OpLe: + return QV4::__qmljs_le; + case V4IR::OpEqual: + return QV4::__qmljs_eq; + case V4IR::OpNotEqual: + return QV4::__qmljs_ne; + case V4IR::OpStrictEqual: + return QV4::__qmljs_se; + case V4IR::OpStrictNotEqual: + return QV4::__qmljs_sne; + case V4IR::OpInstanceof: + return 0; + case V4IR::OpIn: + return 0; + case V4IR::OpAnd: + return 0; + case V4IR::OpOr: + return 0; + default: + assert(!"Unknown AluOp"); + return 0; + } +}; +} // anonymous namespace + +// TODO: extend to optimize out temp-to-temp moves, where the lifetime of one temp ends at that statement. +// To handle that, add a hint when such a move will occur, and add a stmt for the hint. +// Then when asked for a register, check if the active statement is the terminating statement, and if so, apply the hint. +// This generalises the hint usage for Phi removal too, when the phi is passed in there as the current statement. +class QQmlJS::Moth::StackSlotAllocator +{ + QHash _slotForTemp; + QHash _hints; + QVector _activeSlots; + + QHash _intervals; + +public: + StackSlotAllocator(const QList &ranges, int maxTempCount) + : _activeSlots(maxTempCount) + { + _intervals.reserve(ranges.size()); + foreach (const V4IR::LifeTimeInterval &r, ranges) + _intervals[r.temp()] = r; + } + + void addHint(const V4IR::Temp &hintedSlotOfTemp, const V4IR::Temp &newTemp) + { + if (hintedSlotOfTemp.kind != V4IR::Temp::VirtualRegister + || newTemp.kind != V4IR::Temp::VirtualRegister) + return; + + if (_slotForTemp.contains(newTemp) || _hints.contains(newTemp)) + return; + + int hintedSlot = _slotForTemp.value(hintedSlotOfTemp, -1); + Q_ASSERT(hintedSlot >= 0); + _hints[newTemp] = hintedSlot; + } + + int stackSlotFor(V4IR::Temp *t, V4IR::Stmt *currentStmt) { + int idx = _slotForTemp.value(*t, -1); + if (idx == -1) + idx = allocateSlot(t, currentStmt); + Q_ASSERT(idx >= 0); + return idx; + } + +private: + int allocateSlot(V4IR::Temp *t, V4IR::Stmt *currentStmt) { + const V4IR::LifeTimeInterval &interval = _intervals[*t]; + int idx = _hints.value(*t, -1); + if (idx != -1 && _activeSlots[idx] <= currentStmt->id) { + _slotForTemp[*t] = idx; + _activeSlots[idx] = interval.end(); + return idx; + } + + for (int i = 0, ei = _activeSlots.size(); i != ei; ++i) { + if (_activeSlots[i] < currentStmt->id) { + _slotForTemp[*t] = i; + _activeSlots[i] = interval.end(); + return i; + } + } + + return -1; + } +}; + +InstructionSelection::InstructionSelection(QV4::ExecutionEngine *engine, V4IR::Module *module) + : EvalInstructionSelection(engine, module) + , _function(0) + , _vmFunction(0) + , _block(0) + , _codeStart(0) + , _codeNext(0) + , _codeEnd(0) + , _stackSlotAllocator(0) + , _currentStatement(0) +{ +} + +InstructionSelection::~InstructionSelection() +{ +} + +void InstructionSelection::run(QV4::Function *vmFunction, V4IR::Function *function) +{ + V4IR::BasicBlock *block = 0, *nextBlock = 0; + + QHash > patches; + QHash addrs; + + int codeSize = 4096; + uchar *codeStart = new uchar[codeSize]; + memset(codeStart, 0, codeSize); + uchar *codeNext = codeStart; + uchar *codeEnd = codeStart + codeSize; + + qSwap(_function, function); + qSwap(_vmFunction, vmFunction); + qSwap(block, _block); + qSwap(nextBlock, _nextBlock); + qSwap(patches, _patches); + qSwap(addrs, _addrs); + qSwap(codeStart, _codeStart); + qSwap(codeNext, _codeNext); + qSwap(codeEnd, _codeEnd); + + V4IR::Optimizer opt(_function); + opt.run(); + StackSlotAllocator *stackSlotAllocator = 0; + if (opt.isInSSA()) + stackSlotAllocator = new StackSlotAllocator(opt.lifeRanges(), _function->tempCount); + qSwap(_stackSlotAllocator, stackSlotAllocator); + V4IR::Stmt *cs = 0; + qSwap(_currentStatement, cs); + + int locals = frameSize(); + assert(locals >= 0); + + Instruction::Push push; + push.value = quint32(locals); + addInstruction(push); + + for (int i = 0, ei = _function->basicBlocks.size(); i != ei; ++i) { + _block = _function->basicBlocks[i]; + _nextBlock = (i < ei - 1) ? _function->basicBlocks[i + 1] : 0; + _addrs.insert(_block, _codeNext - _codeStart); + + foreach (V4IR::Stmt *s, _block->statements) { + if (s->location.isValid()) { + QV4::LineNumberMapping mapping; + mapping.codeOffset = _codeNext - _codeStart; + mapping.lineNumber = s->location.startLine; + _vmFunction->lineNumberMappings.append(mapping); + } + + if (opt.isInSSA() && s->asTerminator()) { + foreach (const V4IR::Optimizer::SSADeconstructionMove &move, + opt.ssaDeconstructionMoves(_block)) { + Q_ASSERT(move.source->asTemp()); // FIXME: support Const exprs in Phi nodes. + if (move.needsConversion()) + convertType(move.source->asTemp(), move.target); + else + copyValue(move.source->asTemp(), move.target); + } + } + + _currentStatement = s; + s->accept(this); + } + } + + // TODO: patch stack size (the push instruction) + patchJumpAddresses(); + + _vmFunction->code = VME::exec; + _vmFunction->codeData = squeezeCode(); + + if (QV4::Debugging::Debugger *debugger = engine()->debugger) + debugger->setPendingBreakpoints(_vmFunction); + + qSwap(_currentStatement, cs); + qSwap(_stackSlotAllocator, stackSlotAllocator); + delete stackSlotAllocator; + qSwap(_function, function); + qSwap(_vmFunction, vmFunction); + qSwap(block, _block); + qSwap(nextBlock, _nextBlock); + qSwap(patches, _patches); + qSwap(addrs, _addrs); + qSwap(codeStart, _codeStart); + qSwap(codeNext, _codeNext); + qSwap(codeEnd, _codeEnd); + + delete[] codeStart; +} + +void InstructionSelection::callValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result) +{ + Instruction::CallValue call; + prepareCallArgs(args, call.argc, call.args); + call.dest = getParam(value); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result) +{ + // call the property on the loaded base + Instruction::CallProperty call; + call.base = getParam(base); + call.name = identifier(name); + prepareCallArgs(args, call.argc, call.args); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::ExprList *args, V4IR::Temp *result) +{ + // call the property on the loaded base + Instruction::CallElement call; + call.base = getParam(base); + call.index = getParam(index); + prepareCallArgs(args, call.argc, call.args); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::convertType(V4IR::Temp *source, V4IR::Temp *target) +{ + if (_stackSlotAllocator) + _stackSlotAllocator->addHint(*source, *target); + + // FIXME: do something more useful with this info + copyValue(source, target); +} + +void InstructionSelection::constructActivationProperty(V4IR::Name *func, + V4IR::ExprList *args, + V4IR::Temp *result) +{ + Instruction::CreateActivationProperty create; + create.name = identifier(*func->id); + prepareCallArgs(args, create.argc, create.args); + create.result = getResultParam(result); + addInstruction(create); +} + +void InstructionSelection::constructProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result) +{ + Instruction::CreateProperty create; + create.base = getParam(base); + create.name = identifier(name); + prepareCallArgs(args, create.argc, create.args); + create.result = getResultParam(result); + addInstruction(create); +} + +void InstructionSelection::constructValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result) +{ + Instruction::CreateValue create; + create.func = getParam(value); + prepareCallArgs(args, create.argc, create.args); + create.result = getResultParam(result); + addInstruction(create); +} + +void InstructionSelection::loadThisObject(V4IR::Temp *temp) +{ + Instruction::LoadThis load; + load.result = getResultParam(temp); + addInstruction(load); +} + +void InstructionSelection::loadConst(V4IR::Const *sourceConst, V4IR::Temp *targetTemp) +{ + assert(sourceConst); + + Instruction::LoadValue load; + load.value = getParam(sourceConst); + load.result = getResultParam(targetTemp); + addInstruction(load); +} + +void InstructionSelection::loadString(const QString &str, V4IR::Temp *targetTemp) +{ + Instruction::LoadValue load; + load.value = Instr::Param::createValue(QV4::Value::fromString(identifier(str))); + load.result = getResultParam(targetTemp); + addInstruction(load); +} + +void InstructionSelection::loadRegexp(V4IR::RegExp *sourceRegexp, V4IR::Temp *targetTemp) +{ + QV4::Value v = QV4::Value::fromObject(engine()->newRegExpObject( + *sourceRegexp->value, + sourceRegexp->flags)); + _vmFunction->generatedValues.append(v); + + Instruction::LoadValue load; + load.value = Instr::Param::createValue(v); + load.result = getResultParam(targetTemp); + addInstruction(load); +} + +void InstructionSelection::getActivationProperty(const V4IR::Name *name, V4IR::Temp *temp) +{ + Instruction::LoadName load; + load.name = identifier(*name->id); + load.result = getResultParam(temp); + addInstruction(load); +} + +void InstructionSelection::setActivationProperty(V4IR::Temp *source, const QString &targetName) +{ + Instruction::StoreName store; + store.source = getParam(source); + store.name = identifier(targetName); + addInstruction(store); +} + +void InstructionSelection::initClosure(V4IR::Closure *closure, V4IR::Temp *target) +{ + QV4::Function *vmFunc = vmFunction(closure->value); + assert(vmFunc); + Instruction::LoadClosure load; + load.value = vmFunc; + load.result = getResultParam(target); + addInstruction(load); +} + +void InstructionSelection::getProperty(V4IR::Temp *base, const QString &name, V4IR::Temp *target) +{ + Instruction::LoadProperty load; + load.base = getParam(base); + load.name = identifier(name); + load.result = getResultParam(target); + addInstruction(load); +} + +void InstructionSelection::setProperty(V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName) +{ + Instruction::StoreProperty store; + store.base = getParam(targetBase); + store.name = identifier(targetName); + store.source = getParam(source); + addInstruction(store); +} + +void InstructionSelection::getElement(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *target) +{ + Instruction::LoadElement load; + load.base = getParam(base); + load.index = getParam(index); + load.result = getResultParam(target); + addInstruction(load); +} + +void InstructionSelection::setElement(V4IR::Temp *source, V4IR::Temp *targetBase, V4IR::Temp *targetIndex) +{ + Instruction::StoreElement store; + store.base = getParam(targetBase); + store.index = getParam(targetIndex); + store.source = getParam(source); + addInstruction(store); +} + +void InstructionSelection::copyValue(V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp) +{ + if (_stackSlotAllocator) + _stackSlotAllocator->addHint(*sourceTemp, *targetTemp); + + Instruction::MoveTemp move; + move.source = getParam(sourceTemp); + move.result = getResultParam(targetTemp); + if (move.source != move.result) + addInstruction(move); +} + +void InstructionSelection::unop(V4IR::AluOp oper, V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp) +{ + QV4::UnaryOpName op = 0; + switch (oper) { + case V4IR::OpIfTrue: assert(!"unreachable"); break; + case V4IR::OpNot: op = QV4::__qmljs_not; break; + case V4IR::OpUMinus: op = QV4::__qmljs_uminus; break; + case V4IR::OpUPlus: op = QV4::__qmljs_uplus; break; + case V4IR::OpCompl: op = QV4::__qmljs_compl; break; + case V4IR::OpIncrement: op = QV4::__qmljs_increment; break; + case V4IR::OpDecrement: op = QV4::__qmljs_decrement; break; + default: assert(!"unreachable"); break; + } // switch + + if (op) { + Instruction::Unop unop; + unop.alu = op; + unop.source = getParam(sourceTemp); + unop.result = getResultParam(targetTemp); + addInstruction(unop); + } else { + qWarning(" UNOP1"); + } +} + +void InstructionSelection::binop(V4IR::AluOp oper, V4IR::Expr *leftSource, V4IR::Expr *rightSource, V4IR::Temp *target) +{ +#ifdef USE_TYPE_INFO + if (leftSource->type & V4IR::NumberType && rightSource->type & V4IR::NumberType) { + // TODO: add Temp+Const variation on the topic. + switch (oper) { + case V4IR::OpAdd: { + Instruction::AddNumberParams instr; + instr.lhs = getParam(leftSource); + instr.rhs = getParam(rightSource); + instr.result = getResultParam(target); + addInstruction(instr); + } return; + case V4IR::OpMul: { + Instruction::MulNumberParams instr; + instr.lhs = getParam(leftSource); + instr.rhs = getParam(rightSource); + instr.result = getResultParam(target); + addInstruction(instr); + } return; + case V4IR::OpSub: { + Instruction::SubNumberParams instr; + instr.lhs = getParam(leftSource); + instr.rhs = getParam(rightSource); + instr.result = getResultParam(target); + addInstruction(instr); + } return; + default: + break; + } + } +#else // !USE_TYPE_INFO + Q_ASSERT(leftSource->asTemp() && rightSource->asTemp()); +#endif // USE_TYPE_INFO + + if (oper == V4IR::OpInstanceof || oper == V4IR::OpIn || oper == V4IR::OpAdd) { + Instruction::BinopContext binop; + if (oper == V4IR::OpInstanceof) + binop.alu = QV4::__qmljs_instanceof; + else if (oper == V4IR::OpIn) + binop.alu = QV4::__qmljs_in; + else + binop.alu = QV4::__qmljs_add; + binop.lhs = getParam(leftSource); + binop.rhs = getParam(rightSource); + binop.result = getResultParam(target); + addInstruction(binop); + } else { + Instruction::Binop binop; + binop.alu = aluOpFunction(oper); + binop.lhs = getParam(leftSource); + binop.rhs = getParam(rightSource); + binop.result = getResultParam(target); + addInstruction(binop); + } +} + +void InstructionSelection::inplaceNameOp(V4IR::AluOp oper, V4IR::Temp *rightSource, const QString &targetName) +{ + QV4::InplaceBinOpName op = 0; + switch (oper) { + case V4IR::OpBitAnd: op = QV4::__qmljs_inplace_bit_and_name; break; + case V4IR::OpBitOr: op = QV4::__qmljs_inplace_bit_or_name; break; + case V4IR::OpBitXor: op = QV4::__qmljs_inplace_bit_xor_name; break; + case V4IR::OpAdd: op = QV4::__qmljs_inplace_add_name; break; + case V4IR::OpSub: op = QV4::__qmljs_inplace_sub_name; break; + case V4IR::OpMul: op = QV4::__qmljs_inplace_mul_name; break; + case V4IR::OpDiv: op = QV4::__qmljs_inplace_div_name; break; + case V4IR::OpMod: op = QV4::__qmljs_inplace_mod_name; break; + case V4IR::OpLShift: op = QV4::__qmljs_inplace_shl_name; break; + case V4IR::OpRShift: op = QV4::__qmljs_inplace_shr_name; break; + case V4IR::OpURShift: op = QV4::__qmljs_inplace_ushr_name; break; + default: break; + } + + if (op) { + Instruction::InplaceNameOp ieo; + ieo.alu = op; + ieo.name = identifier(targetName); + ieo.source = getParam(rightSource); + addInstruction(ieo); + } +} + +void InstructionSelection::inplaceElementOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBaseTemp, V4IR::Temp *targetIndexTemp) +{ + QV4::InplaceBinOpElement op = 0; + switch (oper) { + case V4IR::OpBitAnd: op = QV4::__qmljs_inplace_bit_and_element; break; + case V4IR::OpBitOr: op = QV4::__qmljs_inplace_bit_or_element; break; + case V4IR::OpBitXor: op = QV4::__qmljs_inplace_bit_xor_element; break; + case V4IR::OpAdd: op = QV4::__qmljs_inplace_add_element; break; + case V4IR::OpSub: op = QV4::__qmljs_inplace_sub_element; break; + case V4IR::OpMul: op = QV4::__qmljs_inplace_mul_element; break; + case V4IR::OpDiv: op = QV4::__qmljs_inplace_div_element; break; + case V4IR::OpMod: op = QV4::__qmljs_inplace_mod_element; break; + case V4IR::OpLShift: op = QV4::__qmljs_inplace_shl_element; break; + case V4IR::OpRShift: op = QV4::__qmljs_inplace_shr_element; break; + case V4IR::OpURShift: op = QV4::__qmljs_inplace_ushr_element; break; + default: break; + } + + Instruction::InplaceElementOp ieo; + ieo.alu = op; + ieo.base = getParam(targetBaseTemp); + ieo.index = getParam(targetIndexTemp); + ieo.source = getParam(source); + addInstruction(ieo); +} + +void InstructionSelection::inplaceMemberOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName) +{ + QV4::InplaceBinOpMember op = 0; + switch (oper) { + case V4IR::OpBitAnd: op = QV4::__qmljs_inplace_bit_and_member; break; + case V4IR::OpBitOr: op = QV4::__qmljs_inplace_bit_or_member; break; + case V4IR::OpBitXor: op = QV4::__qmljs_inplace_bit_xor_member; break; + case V4IR::OpAdd: op = QV4::__qmljs_inplace_add_member; break; + case V4IR::OpSub: op = QV4::__qmljs_inplace_sub_member; break; + case V4IR::OpMul: op = QV4::__qmljs_inplace_mul_member; break; + case V4IR::OpDiv: op = QV4::__qmljs_inplace_div_member; break; + case V4IR::OpMod: op = QV4::__qmljs_inplace_mod_member; break; + case V4IR::OpLShift: op = QV4::__qmljs_inplace_shl_member; break; + case V4IR::OpRShift: op = QV4::__qmljs_inplace_shr_member; break; + case V4IR::OpURShift: op = QV4::__qmljs_inplace_ushr_member; break; + default: break; + } + + Instruction::InplaceMemberOp imo; + imo.alu = op; + imo.base = getParam(targetBase); + imo.member = identifier(targetName); + imo.source = getParam(source); + addInstruction(imo); +} + +void InstructionSelection::prepareCallArgs(V4IR::ExprList *e, quint32 &argc, quint32 &args) +{ + bool singleArgIsTemp = false; + if (e && e->next == 0 && e->expr->asTemp()) { + singleArgIsTemp = e->expr->asTemp()->kind == V4IR::Temp::VirtualRegister; + } + + if (singleArgIsTemp) { + // We pass single arguments as references to the stack, but only if it's not a local or an argument. + argc = 1; + args = getParam(e->expr).index; + } else if (e) { + // We need to move all the temps into the function arg array + int argLocation = outgoingArgumentTempStart(); + assert(argLocation >= 0); + argc = 0; + args = argLocation; + while (e) { + Instruction::MoveTemp move; + move.source = getParam(e->expr); + move.result = Instr::Param::createTemp(argLocation); + addInstruction(move); + ++argLocation; + ++argc; + e = e->next; + } + } else { + argc = 0; + args = 0; + } +} + +void InstructionSelection::visitJump(V4IR::Jump *s) +{ + if (s->target == _nextBlock) + return; + + Instruction::Jump jump; + jump.offset = 0; + ptrdiff_t loc = addInstruction(jump) + (((const char *)&jump.offset) - ((const char *)&jump)); + + _patches[s->target].append(loc); +} + +void InstructionSelection::visitCJump(V4IR::CJump *s) +{ + Instr::Param condition; + if (V4IR::Temp *t = s->cond->asTemp()) { + condition = getResultParam(t); + } else if (V4IR::Binop *b = s->cond->asBinop()) { + condition = getResultParam(0); + Instruction::Binop binop; + binop.alu = aluOpFunction(b->op); + binop.lhs = getParam(b->left); + binop.rhs = getParam(b->right); + binop.result = condition; + addInstruction(binop); + } else { + Q_UNIMPLEMENTED(); + } + + Instruction::CJump jump; + jump.offset = 0; + jump.condition = condition; + ptrdiff_t trueLoc = addInstruction(jump) + (((const char *)&jump.offset) - ((const char *)&jump)); + _patches[s->iftrue].append(trueLoc); + + if (s->iffalse != _nextBlock) { + Instruction::Jump jump; + jump.offset = 0; + ptrdiff_t falseLoc = addInstruction(jump) + (((const char *)&jump.offset) - ((const char *)&jump)); + _patches[s->iffalse].append(falseLoc); + } +} + +void InstructionSelection::visitRet(V4IR::Ret *s) +{ + Instruction::Ret ret; + ret.result = getParam(s->expr); + addInstruction(ret); +} + +void InstructionSelection::visitTry(V4IR::Try *t) +{ + Instruction::EnterTry enterTry; + enterTry.tryOffset = 0; + enterTry.catchOffset = 0; + enterTry.exceptionVarName = identifier(t->exceptionVarName); + enterTry.exceptionVar = getParam(t->exceptionVar); + ptrdiff_t enterTryLoc = addInstruction(enterTry); + + ptrdiff_t tryLoc = enterTryLoc + (((const char *)&enterTry.tryOffset) - ((const char *)&enterTry)); + _patches[t->tryBlock].append(tryLoc); + + ptrdiff_t catchLoc = enterTryLoc + (((const char *)&enterTry.catchOffset) - ((const char *)&enterTry)); + _patches[t->catchBlock].append(catchLoc); +} + +void InstructionSelection::callBuiltinInvalid(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result) +{ + Instruction::CallActivationProperty call; + call.name = identifier(*func->id); + prepareCallArgs(args, call.argc, call.args); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinTypeofMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) +{ + Instruction::CallBuiltinTypeofMember call; + call.base = getParam(base); + call.member = identifier(name); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinTypeofSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) +{ + Instruction::CallBuiltinTypeofSubscript call; + call.base = getParam(base); + call.index = getParam(index); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinTypeofName(const QString &name, V4IR::Temp *result) +{ + Instruction::CallBuiltinTypeofName call; + call.name = identifier(name); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinTypeofValue(V4IR::Temp *value, V4IR::Temp *result) +{ + Instruction::CallBuiltinTypeofValue call; + call.value = getParam(value); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinDeleteMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) +{ + Instruction::CallBuiltinDeleteMember call; + call.base = getParam(base); + call.member = identifier(name); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinDeleteSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) +{ + Instruction::CallBuiltinDeleteSubscript call; + call.base = getParam(base); + call.index = getParam(index); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinDeleteName(const QString &name, V4IR::Temp *result) +{ + Instruction::CallBuiltinDeleteName call; + call.name = identifier(name); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinDeleteValue(V4IR::Temp *result) +{ + Instruction::LoadValue load; + load.value = Instr::Param::createValue(QV4::Value::fromBoolean(false)); + load.result = getResultParam(result); + addInstruction(load); +} + +void InstructionSelection::callBuiltinPostDecrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) +{ + Instruction::CallBuiltinPostDecMember call; + call.base = getParam(base); + call.member = identifier(name); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinPostDecrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) +{ + Instruction::CallBuiltinPostDecSubscript call; + call.base = getParam(base); + call.index = getParam(index); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinPostDecrementName(const QString &name, V4IR::Temp *result) +{ + Instruction::CallBuiltinPostDecName call; + call.name = identifier(name); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinPostDecrementValue(V4IR::Temp *value, V4IR::Temp *result) +{ + Instruction::CallBuiltinPostDecValue call; + call.value = getParam(value); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinPostIncrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) +{ + Instruction::CallBuiltinPostIncMember call; + call.base = getParam(base); + call.member = identifier(name); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinPostIncrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) +{ + Instruction::CallBuiltinPostIncSubscript call; + call.base = getParam(base); + call.index = getParam(index); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinPostIncrementName(const QString &name, V4IR::Temp *result) +{ + Instruction::CallBuiltinPostIncName call; + call.name = identifier(name); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinPostIncrementValue(V4IR::Temp *value, V4IR::Temp *result) +{ + Instruction::CallBuiltinPostIncValue call; + call.value = getParam(value); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinThrow(V4IR::Temp *arg) +{ + Instruction::CallBuiltinThrow call; + call.arg = getParam(arg); + addInstruction(call); +} + +void InstructionSelection::callBuiltinFinishTry() +{ + Instruction::CallBuiltinFinishTry call; + addInstruction(call); +} + +void InstructionSelection::callBuiltinForeachIteratorObject(V4IR::Temp *arg, V4IR::Temp *result) +{ + Instruction::CallBuiltinForeachIteratorObject call; + call.arg = getParam(arg); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinForeachNextPropertyname(V4IR::Temp *arg, V4IR::Temp *result) +{ + Instruction::CallBuiltinForeachNextPropertyName call; + call.arg = getParam(arg); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinPushWithScope(V4IR::Temp *arg) +{ + Instruction::CallBuiltinPushScope call; + call.arg = getParam(arg); + addInstruction(call); +} + +void InstructionSelection::callBuiltinPopScope() +{ + Instruction::CallBuiltinPopScope call; + addInstruction(call); +} + +void InstructionSelection::callBuiltinDeclareVar(bool deletable, const QString &name) +{ + Instruction::CallBuiltinDeclareVar call; + call.isDeletable = deletable; + call.varName = identifier(name); + addInstruction(call); +} + +void InstructionSelection::callBuiltinDefineGetterSetter(V4IR::Temp *object, const QString &name, V4IR::Temp *getter, V4IR::Temp *setter) +{ + Instruction::CallBuiltinDefineGetterSetter call; + call.object = getParam(object); + call.name = identifier(name); + call.getter = getParam(getter); + call.setter = getParam(setter); + addInstruction(call); +} + +void InstructionSelection::callBuiltinDefineProperty(V4IR::Temp *object, const QString &name, V4IR::Temp *value) +{ + Instruction::CallBuiltinDefineProperty call; + call.object = getParam(object); + call.name = identifier(name); + call.value = getParam(value); + addInstruction(call); +} + +void InstructionSelection::callBuiltinDefineArray(V4IR::Temp *result, V4IR::ExprList *args) +{ + Instruction::CallBuiltinDefineArray call; + prepareCallArgs(args, call.argc, call.args); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinDefineObjectLiteral(V4IR::Temp *result, V4IR::ExprList *args) +{ + int argLocation = outgoingArgumentTempStart(); + + QV4::InternalClass *klass = engine()->emptyClass; + V4IR::ExprList *it = args; + while (it) { + V4IR::Name *name = it->expr->asName(); + it = it->next; + + bool isData = it->expr->asConst()->value; + it = it->next; + klass = klass->addMember(identifier(*name->id), isData ? QV4::Attr_Data : QV4::Attr_Accessor); + + Instruction::MoveTemp move; + move.source = getParam(it->expr); + move.result = Instr::Param::createTemp(argLocation); + addInstruction(move); + ++argLocation; + + if (!isData) { + it = it->next; + + Instruction::MoveTemp move; + move.source = getParam(it->expr); + move.result = Instr::Param::createTemp(argLocation); + addInstruction(move); + ++argLocation; + } + + it = it->next; + } + + Instruction::CallBuiltinDefineObjectLiteral call; + call.internalClass = klass; + call.args = outgoingArgumentTempStart(); + call.result = getResultParam(result); + addInstruction(call); +} + +ptrdiff_t InstructionSelection::addInstructionHelper(Instr::Type type, Instr &instr) +{ +#ifdef MOTH_THREADED_INTERPRETER + instr.common.code = VME::instructionJumpTable()[static_cast(type)]; +#else + instr.common.instructionType = type; +#endif + instr.common.breakPoint = 0; + + int instructionSize = Instr::size(type); + if (_codeEnd - _codeNext < instructionSize) { + int currSize = _codeEnd - _codeStart; + uchar *newCode = new uchar[currSize * 2]; + ::memset(newCode + currSize, 0, currSize); + ::memcpy(newCode, _codeStart, currSize); + _codeNext = _codeNext - _codeStart + newCode; + delete[] _codeStart; + _codeStart = newCode; + _codeEnd = _codeStart + currSize * 2; + } + + ::memcpy(_codeNext, reinterpret_cast(&instr), instructionSize); + ptrdiff_t ptrOffset = _codeNext - _codeStart; + _codeNext += instructionSize; + + return ptrOffset; +} + +void InstructionSelection::patchJumpAddresses() +{ + typedef QHash >::ConstIterator PatchIt; + for (PatchIt i = _patches.begin(), ei = _patches.end(); i != ei; ++i) { + Q_ASSERT(_addrs.contains(i.key())); + ptrdiff_t target = _addrs.value(i.key()); + + const QVector &patchList = i.value(); + for (int ii = 0, eii = patchList.count(); ii < eii; ++ii) { + ptrdiff_t patch = patchList.at(ii); + + *((ptrdiff_t *)(_codeStart + patch)) = target - patch; + } + } + + _patches.clear(); + _addrs.clear(); +} + +uchar *InstructionSelection::squeezeCode() const +{ + int codeSize = _codeNext - _codeStart; + uchar *squeezed = new uchar[codeSize]; + ::memcpy(squeezed, _codeStart, codeSize); + return squeezed; +} + +QV4::String *InstructionSelection::identifier(const QString &s) +{ + QV4::String *str = engine()->newIdentifier(s); + _vmFunction->identifiers.append(str); + return str; +} + +Instr::Param InstructionSelection::getParam(V4IR::Expr *e) { + typedef Instr::Param Param; + assert(e); + + if (V4IR::Const *c = e->asConst()) { + return Param::createValue(convertToValue(c)); + } else if (V4IR::Temp *t = e->asTemp()) { + switch (t->kind) { + case V4IR::Temp::Formal: + case V4IR::Temp::ScopedFormal: return Param::createArgument(t->index, t->scope); + case V4IR::Temp::Local: return Param::createLocal(t->index); + case V4IR::Temp::ScopedLocal: return Param::createScopedLocal(t->index, t->scope); + case V4IR::Temp::VirtualRegister: + return Param::createTemp(_stackSlotAllocator ? + _stackSlotAllocator->stackSlotFor(t, _currentStatement) : t->index); + default: + Q_UNIMPLEMENTED(); + return Param(); + } + } else { + Q_UNIMPLEMENTED(); + return Param(); + } +} diff --git a/src/qml/compiler/qv4isel_moth_p.h b/src/qml/compiler/qv4isel_moth_p.h new file mode 100644 index 0000000000..9c17245f67 --- /dev/null +++ b/src/qml/compiler/qv4isel_moth_p.h @@ -0,0 +1,201 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4ISEL_MOTH_P_H +#define QV4ISEL_MOTH_P_H + +#include +#include +#include +#include +#include +#include "qv4instr_moth_p.h" + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { +namespace Moth { + +class StackSlotAllocator; + +class Q_QML_EXPORT InstructionSelection: + public V4IR::IRDecoder, + public EvalInstructionSelection +{ +public: + InstructionSelection(QV4::ExecutionEngine *engine, V4IR::Module *module); + ~InstructionSelection(); + + virtual void run(QV4::Function *vmFunction, V4IR::Function *function); + +protected: + virtual void visitJump(V4IR::Jump *); + virtual void visitCJump(V4IR::CJump *); + virtual void visitRet(V4IR::Ret *); + virtual void visitTry(V4IR::Try *); + + virtual void callBuiltinInvalid(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result); + virtual void callBuiltinTypeofMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result); + virtual void callBuiltinTypeofSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result); + virtual void callBuiltinTypeofName(const QString &name, V4IR::Temp *result); + virtual void callBuiltinTypeofValue(V4IR::Temp *value, V4IR::Temp *result); + virtual void callBuiltinDeleteMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result); + virtual void callBuiltinDeleteSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result); + virtual void callBuiltinDeleteName(const QString &name, V4IR::Temp *result); + virtual void callBuiltinDeleteValue(V4IR::Temp *result); + virtual void callBuiltinPostDecrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result); + virtual void callBuiltinPostDecrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result); + virtual void callBuiltinPostDecrementName(const QString &name, V4IR::Temp *result); + virtual void callBuiltinPostDecrementValue(V4IR::Temp *value, V4IR::Temp *result); + virtual void callBuiltinPostIncrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result); + virtual void callBuiltinPostIncrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result); + virtual void callBuiltinPostIncrementName(const QString &name, V4IR::Temp *result); + virtual void callBuiltinPostIncrementValue(V4IR::Temp *value, V4IR::Temp *result); + virtual void callBuiltinThrow(V4IR::Temp *arg); + virtual void callBuiltinFinishTry(); + virtual void callBuiltinForeachIteratorObject(V4IR::Temp *arg, V4IR::Temp *result); + virtual void callBuiltinForeachNextPropertyname(V4IR::Temp *arg, V4IR::Temp *result); + virtual void callBuiltinPushWithScope(V4IR::Temp *arg); + virtual void callBuiltinPopScope(); + virtual void callBuiltinDeclareVar(bool deletable, const QString &name); + virtual void callBuiltinDefineGetterSetter(V4IR::Temp *object, const QString &name, V4IR::Temp *getter, V4IR::Temp *setter); + virtual void callBuiltinDefineProperty(V4IR::Temp *object, const QString &name, V4IR::Temp *value); + virtual void callBuiltinDefineArray(V4IR::Temp *result, V4IR::ExprList *args); + virtual void callBuiltinDefineObjectLiteral(V4IR::Temp *result, V4IR::ExprList *args); + virtual void callValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result); + virtual void callProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result); + virtual void callSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::ExprList *args, V4IR::Temp *result); + virtual void convertType(V4IR::Temp *source, V4IR::Temp *target); + virtual void constructActivationProperty(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result); + virtual void constructProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result); + virtual void constructValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result); + virtual void loadThisObject(V4IR::Temp *temp); + virtual void loadConst(V4IR::Const *sourceConst, V4IR::Temp *targetTemp); + virtual void loadString(const QString &str, V4IR::Temp *targetTemp); + virtual void loadRegexp(V4IR::RegExp *sourceRegexp, V4IR::Temp *targetTemp); + virtual void getActivationProperty(const V4IR::Name *name, V4IR::Temp *temp); + virtual void setActivationProperty(V4IR::Temp *source, const QString &targetName); + virtual void initClosure(V4IR::Closure *closure, V4IR::Temp *target); + virtual void getProperty(V4IR::Temp *base, const QString &name, V4IR::Temp *target); + virtual void setProperty(V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName); + virtual void getElement(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *target); + virtual void setElement(V4IR::Temp *source, V4IR::Temp *targetBase, V4IR::Temp *targetIndex); + virtual void copyValue(V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp); + virtual void unop(V4IR::AluOp oper, V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp); + virtual void binop(V4IR::AluOp oper, V4IR::Expr *leftSource, V4IR::Expr *rightSource, V4IR::Temp *target); + virtual void inplaceNameOp(V4IR::AluOp oper, V4IR::Temp *rightSource, const QString &targetName); + virtual void inplaceElementOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBaseTemp, V4IR::Temp *targetIndexTemp); + virtual void inplaceMemberOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName); + +private: + struct Instruction { +#define MOTH_INSTR_DATA_TYPEDEF(I, FMT) typedef InstrData I; + FOR_EACH_MOTH_INSTR(MOTH_INSTR_DATA_TYPEDEF) +#undef MOTH_INSTR_DATA_TYPEDEF + private: + Instruction(); + }; + + Instr::Param getParam(V4IR::Expr *e); + + Instr::Param getResultParam(V4IR::Temp *result) + { + if (result) + return getParam(result); + else + return Instr::Param::createTemp(scratchTempIndex()); + } + + void simpleMove(V4IR::Move *); + void prepareCallArgs(V4IR::ExprList *, quint32 &, quint32 &); + + int outgoingArgumentTempStart() const { return _function->tempCount; } + int scratchTempIndex() const { return outgoingArgumentTempStart() + _function->maxNumberOfArguments; } + int frameSize() const { return scratchTempIndex() + 1; } + + template + inline ptrdiff_t addInstruction(const InstrData &data); + ptrdiff_t addInstructionHelper(Instr::Type type, Instr &instr); + void patchJumpAddresses(); + uchar *squeezeCode() const; + + QV4::String *identifier(const QString &s); + + V4IR::Function *_function; + QV4::Function *_vmFunction; + V4IR::BasicBlock *_block; + V4IR::BasicBlock *_nextBlock; + + QHash > _patches; + QHash _addrs; + + uchar *_codeStart; + uchar *_codeNext; + uchar *_codeEnd; + + StackSlotAllocator *_stackSlotAllocator; + V4IR::Stmt *_currentStatement; +}; + +class Q_QML_EXPORT ISelFactory: public EvalISelFactory +{ +public: + virtual ~ISelFactory() {} + virtual EvalInstructionSelection *create(QV4::ExecutionEngine *engine, V4IR::Module *module) + { return new InstructionSelection(engine, module); } + virtual bool jitCompileRegexps() const + { return false; } +}; + +template +ptrdiff_t InstructionSelection::addInstruction(const InstrData &data) +{ + Instr genericInstr; + InstrMeta::setData(genericInstr, data); + return addInstructionHelper(static_cast(InstrT), genericInstr); +} + +} // namespace Moth +} // namespace QQmlJS + +QT_END_NAMESPACE + +#endif // QV4ISEL_MOTH_P_H diff --git a/src/qml/compiler/qv4isel_p.cpp b/src/qml/compiler/qv4isel_p.cpp new file mode 100644 index 0000000000..ca8d249f9f --- /dev/null +++ b/src/qml/compiler/qv4isel_p.cpp @@ -0,0 +1,440 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4debugging_p.h" +#include "qv4engine_p.h" +#include "qv4jsir_p.h" +#include "qv4isel_p.h" +#include "qv4isel_util_p.h" +#include "qv4functionobject_p.h" +#include "qv4function_p.h" + +#include + +#include + +namespace { +QTextStream qout(stderr, QIODevice::WriteOnly); +} // anonymous namespace + +using namespace QQmlJS; +using namespace QQmlJS::V4IR; + +EvalInstructionSelection::EvalInstructionSelection(QV4::ExecutionEngine *engine, Module *module) + : _engine(engine) + , useFastLookups(true) +{ + assert(engine); + assert(module); + + createFunctionMapping(0, module->rootFunction); + foreach (V4IR::Function *f, module->functions) { + assert(_irToVM.contains(f)); + } +} + +EvalInstructionSelection::~EvalInstructionSelection() +{} + +EvalISelFactory::~EvalISelFactory() +{} + +QV4::Function *EvalInstructionSelection::createFunctionMapping(QV4::Function *outer, Function *irFunction) +{ + QV4::Function *vmFunction = _engine->newFunction(irFunction->name ? *irFunction->name : QString()); + _irToVM.insert(irFunction, vmFunction); + + vmFunction->hasDirectEval = irFunction->hasDirectEval; + vmFunction->usesArgumentsObject = irFunction->usesArgumentsObject; + vmFunction->hasNestedFunctions = !irFunction->nestedFunctions.isEmpty(); + vmFunction->isStrict = irFunction->isStrict; + vmFunction->outer = outer; + vmFunction->isNamedExpression = irFunction->isNamedExpression; + vmFunction->sourceFile = irFunction->sourceFile; + + if (outer) + outer->nestedFunctions.append(vmFunction); + + foreach (const QString *formal, irFunction->formals) + if (formal) + vmFunction->formals.append(_engine->newString(*formal)); + foreach (const QString *local, irFunction->locals) + if (local) + vmFunction->locals.append(_engine->newString(*local)); + + foreach (V4IR::Function *function, irFunction->nestedFunctions) + createFunctionMapping(vmFunction, function); + + return vmFunction; +} + +QV4::Function *EvalInstructionSelection::vmFunction(Function *f) { + QV4::Function *function = _irToVM[f]; + if (!function->code) + run(function, f); + return function; +} + +void IRDecoder::visitMove(V4IR::Move *s) +{ + if (s->op == V4IR::OpInvalid) { + if (V4IR::Name *n = s->target->asName()) { + if (s->source->asTemp()) { + setActivationProperty(s->source->asTemp(), *n->id); + return; + } + } else if (V4IR::Temp *t = s->target->asTemp()) { + if (V4IR::Name *n = s->source->asName()) { + if (*n->id == QStringLiteral("this")) // TODO: `this' should be a builtin. + loadThisObject(t); + else + getActivationProperty(n, t); + return; + } else if (V4IR::Const *c = s->source->asConst()) { + loadConst(c, t); + return; + } else if (V4IR::Temp *t2 = s->source->asTemp()) { + copyValue(t2, t); + return; + } else if (V4IR::String *str = s->source->asString()) { + loadString(*str->value, t); + return; + } else if (V4IR::RegExp *re = s->source->asRegExp()) { + loadRegexp(re, t); + return; + } else if (V4IR::Closure *clos = s->source->asClosure()) { + initClosure(clos, t); + return; + } else if (V4IR::New *ctor = s->source->asNew()) { + if (Name *func = ctor->base->asName()) { + constructActivationProperty(func, ctor->args, t); + return; + } else if (V4IR::Member *member = ctor->base->asMember()) { + constructProperty(member->base->asTemp(), *member->name, ctor->args, t); + return; + } else if (V4IR::Temp *value = ctor->base->asTemp()) { + constructValue(value, ctor->args, t); + return; + } + } else if (V4IR::Member *m = s->source->asMember()) { + if (V4IR::Temp *base = m->base->asTemp()) { + getProperty(base, *m->name, t); + return; + } + } else if (V4IR::Subscript *ss = s->source->asSubscript()) { + getElement(ss->base->asTemp(), ss->index->asTemp(), t); + return; + } else if (V4IR::Unop *u = s->source->asUnop()) { + if (V4IR::Temp *e = u->expr->asTemp()) { + unop(u->op, e, t); + return; + } + } else if (V4IR::Binop *b = s->source->asBinop()) { + binop(b->op, b->left, b->right, t); + return; + } else if (V4IR::Call *c = s->source->asCall()) { + if (c->base->asName()) { + callBuiltin(c, t); + return; + } else if (Member *member = c->base->asMember()) { + Q_ASSERT(member->base->asTemp()); + callProperty(member->base->asTemp(), *member->name, c->args, t); + return; + } else if (Subscript *s = c->base->asSubscript()) { + Q_ASSERT(s->base->asTemp()); + Q_ASSERT(s->index->asTemp()); + callSubscript(s->base->asTemp(), s->index->asTemp(), c->args, t); + return; + } else if (V4IR::Temp *value = c->base->asTemp()) { + callValue(value, c->args, t); + return; + } + } else if (V4IR::Convert *c = s->source->asConvert()) { + Q_ASSERT(c->expr->asTemp()); + convertType(c->expr->asTemp(), t); + return; + } + } else if (V4IR::Member *m = s->target->asMember()) { + if (V4IR::Temp *base = m->base->asTemp()) { + if (s->source->asTemp()) { + setProperty(s->source->asTemp(), base, *m->name); + return; + } + } + } else if (V4IR::Subscript *ss = s->target->asSubscript()) { + if (s->source->asTemp()) { + setElement(s->source->asTemp(), ss->base->asTemp(), ss->index->asTemp()); + return; + } + } + } else { + // inplace assignment, e.g. x += 1, ++x, ... + if (V4IR::Temp *t = s->target->asTemp()) { + if (s->source->asTemp()) { + binop(s->op, t, s->source->asTemp(), t); + return; + } + } else if (V4IR::Name *n = s->target->asName()) { + if (s->source->asTemp()) { + inplaceNameOp(s->op, s->source->asTemp(), *n->id); + return; + } + } else if (V4IR::Subscript *ss = s->target->asSubscript()) { + if (s->source->asTemp()) { + inplaceElementOp(s->op, s->source->asTemp(), ss->base->asTemp(), + ss->index->asTemp()); + return; + } + } else if (V4IR::Member *m = s->target->asMember()) { + if (s->source->asTemp()) { + inplaceMemberOp(s->op, s->source->asTemp(), m->base->asTemp(), *m->name); + return; + } + } + } + + // For anything else...: + Q_UNIMPLEMENTED(); + s->dump(qout, V4IR::Stmt::MIR); + qout << endl; + assert(!"TODO"); +} + +IRDecoder::~IRDecoder() +{ +} + +void IRDecoder::visitExp(V4IR::Exp *s) +{ + if (V4IR::Call *c = s->expr->asCall()) { + // These are calls where the result is ignored. + if (c->base->asName()) { + callBuiltin(c, 0); + } else if (Temp *value = c->base->asTemp()) { + callValue(value, c->args, 0); + } else if (Member *member = c->base->asMember()) { + Q_ASSERT(member->base->asTemp()); + callProperty(member->base->asTemp(), *member->name, c->args, 0); + } else if (Subscript *s = c->base->asSubscript()) { + Q_ASSERT(s->base->asTemp()); + Q_ASSERT(s->index->asTemp()); + callSubscript(s->base->asTemp(), s->index->asTemp(), c->args, 0); + } else { + Q_UNIMPLEMENTED(); + } + } else { + Q_UNIMPLEMENTED(); + } +} + +void IRDecoder::callBuiltin(V4IR::Call *call, V4IR::Temp *result) +{ + V4IR::Name *baseName = call->base->asName(); + assert(baseName != 0); + + switch (baseName->builtin) { + case V4IR::Name::builtin_invalid: + callBuiltinInvalid(baseName, call->args, result); + return; + + case V4IR::Name::builtin_typeof: { + if (V4IR::Member *m = call->args->expr->asMember()) { + callBuiltinTypeofMember(m->base->asTemp(), *m->name, result); + return; + } else if (V4IR::Subscript *ss = call->args->expr->asSubscript()) { + callBuiltinTypeofSubscript(ss->base->asTemp(), ss->index->asTemp(), result); + return; + } else if (V4IR::Name *n = call->args->expr->asName()) { + callBuiltinTypeofName(*n->id, result); + return; + } else if (V4IR::Temp *arg = call->args->expr->asTemp()){ + assert(arg != 0); + callBuiltinTypeofValue(arg, result); + return; + } + } break; + + case V4IR::Name::builtin_delete: { + if (V4IR::Member *m = call->args->expr->asMember()) { + callBuiltinDeleteMember(m->base->asTemp(), *m->name, result); + return; + } else if (V4IR::Subscript *ss = call->args->expr->asSubscript()) { + callBuiltinDeleteSubscript(ss->base->asTemp(), ss->index->asTemp(), result); + return; + } else if (V4IR::Name *n = call->args->expr->asName()) { + callBuiltinDeleteName(*n->id, result); + return; + } else if (call->args->expr->asTemp()){ + // TODO: should throw in strict mode + callBuiltinDeleteValue(result); + return; + } + } break; + + case V4IR::Name::builtin_postincrement: { + if (V4IR::Member *m = call->args->expr->asMember()) { + callBuiltinPostIncrementMember(m->base->asTemp(), *m->name, result); + return; + } else if (V4IR::Subscript *ss = call->args->expr->asSubscript()) { + callBuiltinPostIncrementSubscript(ss->base->asTemp(), ss->index->asTemp(), result); + return; + } else if (V4IR::Name *n = call->args->expr->asName()) { + callBuiltinPostIncrementName(*n->id, result); + return; + } else if (V4IR::Temp *arg = call->args->expr->asTemp()){ + assert(arg != 0); + callBuiltinPostIncrementValue(arg, result); + return; + } + } break; + + case V4IR::Name::builtin_postdecrement: { + if (V4IR::Member *m = call->args->expr->asMember()) { + callBuiltinPostDecrementMember(m->base->asTemp(), *m->name, result); + return; + } else if (V4IR::Subscript *ss = call->args->expr->asSubscript()) { + callBuiltinPostDecrementSubscript(ss->base->asTemp(), ss->index->asTemp(), result); + return; + } else if (V4IR::Name *n = call->args->expr->asName()) { + callBuiltinPostDecrementName(*n->id, result); + return; + } else if (V4IR::Temp *arg = call->args->expr->asTemp()){ + assert(arg != 0); + callBuiltinPostDecrementValue(arg, result); + return; + } + } break; + + case V4IR::Name::builtin_throw: { + V4IR::Temp *arg = call->args->expr->asTemp(); + assert(arg != 0); + callBuiltinThrow(arg); + } return; + + case V4IR::Name::builtin_finish_try: + callBuiltinFinishTry(); + return; + + case V4IR::Name::builtin_foreach_iterator_object: { + V4IR::Temp *arg = call->args->expr->asTemp(); + assert(arg != 0); + callBuiltinForeachIteratorObject(arg, result); + } return; + + case V4IR::Name::builtin_foreach_next_property_name: { + V4IR::Temp *arg = call->args->expr->asTemp(); + assert(arg != 0); + callBuiltinForeachNextPropertyname(arg, result); + } return; + case V4IR::Name::builtin_push_with_scope: { + V4IR::Temp *arg = call->args->expr->asTemp(); + assert(arg != 0); + callBuiltinPushWithScope(arg); + } return; + + case V4IR::Name::builtin_pop_scope: + callBuiltinPopScope(); + return; + + case V4IR::Name::builtin_declare_vars: { + if (!call->args) + return; + V4IR::Const *deletable = call->args->expr->asConst(); + assert(deletable->type == V4IR::BoolType); + for (V4IR::ExprList *it = call->args->next; it; it = it->next) { + V4IR::Name *arg = it->expr->asName(); + assert(arg != 0); + callBuiltinDeclareVar(deletable->value != 0, *arg->id); + } + } return; + + case V4IR::Name::builtin_define_getter_setter: { + if (!call->args) + return; + V4IR::ExprList *args = call->args; + V4IR::Temp *object = args->expr->asTemp(); + assert(object); + args = args->next; + assert(args); + V4IR::Name *name = args->expr->asName(); + args = args->next; + assert(args); + V4IR::Temp *getter = args->expr->asTemp(); + args = args->next; + assert(args); + V4IR::Temp *setter = args->expr->asTemp(); + + callBuiltinDefineGetterSetter(object, *name->id, getter, setter); + } return; + + case V4IR::Name::builtin_define_property: { + if (!call->args) + return; + V4IR::ExprList *args = call->args; + V4IR::Temp *object = args->expr->asTemp(); + assert(object); + args = args->next; + assert(args); + V4IR::Name *name = args->expr->asName(); + args = args->next; + assert(args); + V4IR::Temp *value = args->expr->asTemp(); + + callBuiltinDefineProperty(object, *name->id, value); + } return; + + case V4IR::Name::builtin_define_array: + callBuiltinDefineArray(result, call->args); + return; + + case V4IR::Name::builtin_define_object_literal: + callBuiltinDefineObjectLiteral(result, call->args); + return; + + default: + break; + } + + Q_UNIMPLEMENTED(); + call->dump(qout); qout << endl; + assert(!"TODO!"); + Q_UNREACHABLE(); +} diff --git a/src/qml/compiler/qv4isel_p.h b/src/qml/compiler/qv4isel_p.h new file mode 100644 index 0000000000..965caf2cba --- /dev/null +++ b/src/qml/compiler/qv4isel_p.h @@ -0,0 +1,165 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4ISEL_P_H +#define QV4ISEL_P_H + +#include "private/qv4global_p.h" +#include "qv4jsir_p.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +namespace QV4 { +struct ExecutionEngine; +struct Function; +} + +namespace QQmlJS { + +class Q_QML_EXPORT EvalInstructionSelection +{ +public: + EvalInstructionSelection(QV4::ExecutionEngine *engine, V4IR::Module *module); + virtual ~EvalInstructionSelection() = 0; + + QV4::Function *vmFunction(V4IR::Function *f); + + void setUseFastLookups(bool b) { useFastLookups = b; } + +protected: + QV4::Function *createFunctionMapping(QV4::Function *outer, V4IR::Function *irFunction); + QV4::ExecutionEngine *engine() const { return _engine; } + virtual void run(QV4::Function *vmFunction, V4IR::Function *function) = 0; + +private: + QV4::ExecutionEngine *_engine; + QHash _irToVM; +protected: + bool useFastLookups; +}; + +class Q_QML_EXPORT EvalISelFactory +{ +public: + virtual ~EvalISelFactory() = 0; + virtual EvalInstructionSelection *create(QV4::ExecutionEngine *engine, V4IR::Module *module) = 0; + virtual bool jitCompileRegexps() const = 0; +}; + +namespace V4IR { +class Q_QML_EXPORT IRDecoder: protected V4IR::StmtVisitor +{ +public: + virtual ~IRDecoder() = 0; + + virtual void visitPhi(V4IR::Phi *) {} + +public: // visitor methods for StmtVisitor: + virtual void visitMove(V4IR::Move *s); + virtual void visitExp(V4IR::Exp *s); + +public: // to implement by subclasses: + virtual void callBuiltinInvalid(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result) = 0; + virtual void callBuiltinTypeofMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) = 0; + virtual void callBuiltinTypeofSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) = 0; + virtual void callBuiltinTypeofName(const QString &name, V4IR::Temp *result) = 0; + virtual void callBuiltinTypeofValue(V4IR::Temp *value, V4IR::Temp *result) = 0; + virtual void callBuiltinDeleteMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) = 0; + virtual void callBuiltinDeleteSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) = 0; + virtual void callBuiltinDeleteName(const QString &name, V4IR::Temp *result) = 0; + virtual void callBuiltinDeleteValue(V4IR::Temp *result) = 0; + virtual void callBuiltinPostDecrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) = 0; + virtual void callBuiltinPostDecrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) = 0; + virtual void callBuiltinPostDecrementName(const QString &name, V4IR::Temp *result) = 0; + virtual void callBuiltinPostDecrementValue(V4IR::Temp *value, V4IR::Temp *result) = 0; + virtual void callBuiltinPostIncrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) = 0; + virtual void callBuiltinPostIncrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) = 0; + virtual void callBuiltinPostIncrementName(const QString &name, V4IR::Temp *result) = 0; + virtual void callBuiltinPostIncrementValue(V4IR::Temp *value, V4IR::Temp *result) = 0; + virtual void callBuiltinThrow(V4IR::Temp *arg) = 0; + virtual void callBuiltinFinishTry() = 0; + virtual void callBuiltinForeachIteratorObject(V4IR::Temp *arg, V4IR::Temp *result) = 0; + virtual void callBuiltinForeachNextPropertyname(V4IR::Temp *arg, V4IR::Temp *result) = 0; + virtual void callBuiltinPushWithScope(V4IR::Temp *arg) = 0; + virtual void callBuiltinPopScope() = 0; + virtual void callBuiltinDeclareVar(bool deletable, const QString &name) = 0; + virtual void callBuiltinDefineGetterSetter(V4IR::Temp *object, const QString &name, V4IR::Temp *getter, V4IR::Temp *setter) = 0; + virtual void callBuiltinDefineProperty(V4IR::Temp *object, const QString &name, V4IR::Temp *value) = 0; + virtual void callBuiltinDefineArray(V4IR::Temp *result, V4IR::ExprList *args) = 0; + virtual void callBuiltinDefineObjectLiteral(V4IR::Temp *result, V4IR::ExprList *args) = 0; + virtual void callValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result) = 0; + virtual void callProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result) = 0; + virtual void callSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::ExprList *args, V4IR::Temp *result) = 0; + virtual void convertType(V4IR::Temp *source, V4IR::Temp *target) = 0; + virtual void constructActivationProperty(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result) = 0; + virtual void constructProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result) = 0; + virtual void constructValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result) = 0; + virtual void loadThisObject(V4IR::Temp *temp) = 0; + virtual void loadConst(V4IR::Const *sourceConst, V4IR::Temp *targetTemp) = 0; + virtual void loadString(const QString &str, V4IR::Temp *targetTemp) = 0; + virtual void loadRegexp(V4IR::RegExp *sourceRegexp, V4IR::Temp *targetTemp) = 0; + virtual void getActivationProperty(const V4IR::Name *name, V4IR::Temp *temp) = 0; + virtual void setActivationProperty(V4IR::Temp *source, const QString &targetName) = 0; + virtual void initClosure(V4IR::Closure *closure, V4IR::Temp *target) = 0; + virtual void getProperty(V4IR::Temp *base, const QString &name, V4IR::Temp *target) = 0; + virtual void setProperty(V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName) = 0; + virtual void getElement(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *target) = 0; + virtual void setElement(V4IR::Temp *source, V4IR::Temp *targetBase, V4IR::Temp *targetIndex) = 0; + virtual void copyValue(V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp) = 0; + virtual void unop(V4IR::AluOp oper, V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp) = 0; + virtual void binop(V4IR::AluOp oper, V4IR::Expr *leftSource, V4IR::Expr *rightSource, V4IR::Temp *target) = 0; + virtual void inplaceNameOp(V4IR::AluOp oper, V4IR::Temp *rightSource, const QString &targetName) = 0; + virtual void inplaceElementOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBaseTemp, V4IR::Temp *targetIndexTemp) = 0; + virtual void inplaceMemberOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName) = 0; + +private: + void callBuiltin(V4IR::Call *c, V4IR::Temp *temp); +}; +} // namespace IR + +} // namespace QQmlJS + +QT_END_NAMESPACE + +#endif // QV4ISEL_P_H diff --git a/src/qml/compiler/qv4isel_util_p.h b/src/qml/compiler/qv4isel_util_p.h new file mode 100644 index 0000000000..a422150dd0 --- /dev/null +++ b/src/qml/compiler/qv4isel_util_p.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4ISEL_UTIL_P_H +#define QV4ISEL_UTIL_P_H + +#include "private/qv4runtime_p.h" +#include "qv4jsir_p.h" + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { + +inline QV4::Value convertToValue(V4IR::Const *c) +{ + switch (c->type) { + case V4IR::MissingType: + return QV4::Value::emptyValue(); + case V4IR::NullType: + return QV4::Value::nullValue(); + case V4IR::UndefinedType: + return QV4::Value::undefinedValue(); + case V4IR::BoolType: + return QV4::Value::fromBoolean(c->value != 0); + case V4IR::SInt32Type: + return QV4::Value::fromInt32(int(c->value)); + case V4IR::UInt32Type: + return QV4::Value::fromUInt32(unsigned(c->value)); + case V4IR::DoubleType: + return QV4::Value::fromDouble(c->value); + case V4IR::NumberType: { + int ival = (int)c->value; + // +0 != -0, so we need to convert to double when negating 0 + if (ival == c->value && !(c->value == 0 && isNegative(c->value))) { + return QV4::Value::fromInt32(ival); + } else { + return QV4::Value::fromDouble(c->value); + } + } + default: + Q_UNREACHABLE(); + } +} + +} // namespace QQmlJS + +QT_END_NAMESPACE + +#endif // QV4ISEL_UTIL_P_H diff --git a/src/qml/compiler/qv4jsir.cpp b/src/qml/compiler/qv4jsir.cpp new file mode 100644 index 0000000000..7f8d257429 --- /dev/null +++ b/src/qml/compiler/qv4jsir.cpp @@ -0,0 +1,1024 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4jsir_p.h" +#include + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { +namespace V4IR { + +QString typeName(Type t) +{ + switch (t) { + case UnknownType: return QStringLiteral(""); + case MissingType: return QStringLiteral("missing"); + case UndefinedType: return QStringLiteral("undefined"); + case NullType: return QStringLiteral("null"); + case BoolType: return QStringLiteral("bool"); + case UInt32Type: return QStringLiteral("uint32"); + case SInt32Type: return QStringLiteral("int32"); + case DoubleType: return QStringLiteral("double"); + case NumberType: return QStringLiteral("number"); + case StringType: return QStringLiteral("string"); + case ObjectType: return QStringLiteral("object"); + default: return QStringLiteral("multiple"); + } +} + +const char *opname(AluOp op) +{ + switch (op) { + case OpInvalid: return "?"; + + case OpIfTrue: return "(bool)"; + case OpNot: return "!"; + case OpUMinus: return "-"; + case OpUPlus: return "+"; + case OpCompl: return "~"; + case OpIncrement: return "++"; + case OpDecrement: return "--"; + + case OpBitAnd: return "&"; + case OpBitOr: return "|"; + case OpBitXor: return "^"; + + case OpAdd: return "+"; + case OpSub: return "-"; + case OpMul: return "*"; + case OpDiv: return "/"; + case OpMod: return "%"; + + case OpLShift: return "<<"; + case OpRShift: return ">>"; + case OpURShift: return ">>>"; + + case OpGt: return ">"; + case OpLt: return "<"; + case OpGe: return ">="; + case OpLe: return "<="; + case OpEqual: return "=="; + case OpNotEqual: return "!="; + case OpStrictEqual: return "==="; + case OpStrictNotEqual: return "!=="; + + case OpInstanceof: return "instanceof"; + case OpIn: return "in"; + + case OpAnd: return "&&"; + case OpOr: return "||"; + + default: return "?"; + + } // switch +} + +AluOp binaryOperator(int op) +{ + switch (static_cast(op)) { + case QSOperator::Add: return OpAdd; + case QSOperator::And: return OpAnd; + case QSOperator::BitAnd: return OpBitAnd; + case QSOperator::BitOr: return OpBitOr; + case QSOperator::BitXor: return OpBitXor; + case QSOperator::Div: return OpDiv; + case QSOperator::Equal: return OpEqual; + case QSOperator::Ge: return OpGe; + case QSOperator::Gt: return OpGt; + case QSOperator::Le: return OpLe; + case QSOperator::LShift: return OpLShift; + case QSOperator::Lt: return OpLt; + case QSOperator::Mod: return OpMod; + case QSOperator::Mul: return OpMul; + case QSOperator::NotEqual: return OpNotEqual; + case QSOperator::Or: return OpOr; + case QSOperator::RShift: return OpRShift; + case QSOperator::StrictEqual: return OpStrictEqual; + case QSOperator::StrictNotEqual: return OpStrictNotEqual; + case QSOperator::Sub: return OpSub; + case QSOperator::URShift: return OpURShift; + case QSOperator::InstanceOf: return OpInstanceof; + case QSOperator::In: return OpIn; + default: return OpInvalid; + } +} + +struct RemoveSharedExpressions: V4IR::StmtVisitor, V4IR::ExprVisitor +{ + CloneExpr clone; + QSet subexpressions; // contains all the non-cloned subexpressions in the given function + Expr *uniqueExpr; + + RemoveSharedExpressions(): uniqueExpr(0) {} + + void operator()(V4IR::Function *function) + { + subexpressions.clear(); + + foreach (BasicBlock *block, function->basicBlocks) { + clone.setBasicBlock(block); + + foreach (Stmt *s, block->statements) { + s->accept(this); + } + } + } + + template + _Expr *cleanup(_Expr *expr) + { + if (subexpressions.contains(expr)) { + // the cloned expression is unique by definition + // so we don't need to add it to `subexpressions'. + return clone(expr); + } + + subexpressions.insert(expr); + V4IR::Expr *e = expr; + qSwap(uniqueExpr, e); + expr->accept(this); + qSwap(uniqueExpr, e); + return static_cast<_Expr *>(e); + } + + // statements + virtual void visitExp(Exp *s) + { + s->expr = cleanup(s->expr); + } + + virtual void visitMove(Move *s) + { + s->target = cleanup(s->target); + s->source = cleanup(s->source); + } + + virtual void visitJump(Jump *) + { + // nothing to do for Jump statements + } + + virtual void visitCJump(CJump *s) + { + s->cond = cleanup(s->cond); + } + + virtual void visitRet(Ret *s) + { + s->expr = cleanup(s->expr); + } + + virtual void visitTry(Try *) + { + // nothing to do for Try statements + } + + virtual void visitPhi(V4IR::Phi *) { Q_UNIMPLEMENTED(); abort(); } + + // expressions + virtual void visitConst(Const *) {} + virtual void visitString(String *) {} + virtual void visitRegExp(RegExp *) {} + virtual void visitName(Name *) {} + virtual void visitTemp(Temp *) {} + virtual void visitClosure(Closure *) {} + + virtual void visitConvert(Convert *e) + { + e->expr = cleanup(e->expr); + } + + virtual void visitUnop(Unop *e) + { + e->expr = cleanup(e->expr); + } + + virtual void visitBinop(Binop *e) + { + e->left = cleanup(e->left); + e->right = cleanup(e->right); + } + + virtual void visitCall(Call *e) + { + e->base = cleanup(e->base); + for (V4IR::ExprList *it = e->args; it; it = it->next) + it->expr = cleanup(it->expr); + } + + virtual void visitNew(New *e) + { + e->base = cleanup(e->base); + for (V4IR::ExprList *it = e->args; it; it = it->next) + it->expr = cleanup(it->expr); + } + + virtual void visitSubscript(Subscript *e) + { + e->base = cleanup(e->base); + e->index = cleanup(e->index); + } + + virtual void visitMember(Member *e) + { + e->base = cleanup(e->base); + } +}; + +static QString dumpStart(const Expr *e) { + if (e->type == UnknownType) +// return QStringLiteral("**UNKNOWN**"); + return QString(); + else + return typeName(e->type) + QStringLiteral("{"); +} + +static const char *dumpEnd(const Expr *e) { + if (e->type == UnknownType) + return ""; + else + return "}"; +} + +void Const::dump(QTextStream &out) const +{ + if (type != UndefinedType && type != NullType) + out << dumpStart(this); + switch (type) { + case QQmlJS::V4IR::UndefinedType: + out << "undefined"; + break; + case QQmlJS::V4IR::NullType: + out << "null"; + break; + case QQmlJS::V4IR::BoolType: + out << (value ? "true" : "false"); + break; + case QQmlJS::V4IR::MissingType: + out << "missing"; + break; + default: + out << QString::number(value, 'g', 16); + break; + } + if (type != UndefinedType && type != NullType) + out << dumpEnd(this); +} + +void String::dump(QTextStream &out) const +{ + out << '"' << escape(*value) << '"'; +} + +QString String::escape(const QString &s) +{ + QString r; + for (int i = 0; i < s.length(); ++i) { + const QChar ch = s.at(i); + if (ch == QLatin1Char('\n')) + r += QStringLiteral("\\n"); + else if (ch == QLatin1Char('\r')) + r += QStringLiteral("\\r"); + else if (ch == QLatin1Char('\\')) + r += QStringLiteral("\\\\"); + else if (ch == QLatin1Char('"')) + r += QStringLiteral("\\\""); + else if (ch == QLatin1Char('\'')) + r += QStringLiteral("\\'"); + else + r += ch; + } + return r; +} + +void RegExp::dump(QTextStream &out) const +{ + char f[3]; + int i = 0; + if (flags & RegExp_Global) + f[i++] = 'g'; + if (flags & RegExp_IgnoreCase) + f[i++] = 'i'; + if (flags & RegExp_Multiline) + f[i++] = 'm'; + f[i] = 0; + + out << '/' << *value << '/' << f; +} + +void Name::initGlobal(const QString *id, quint32 line, quint32 column) +{ + this->id = id; + this->builtin = builtin_invalid; + this->global = true; + this->line = line; + this->column = column; +} + +void Name::init(const QString *id, quint32 line, quint32 column) +{ + this->id = id; + this->builtin = builtin_invalid; + this->global = false; + this->line = line; + this->column = column; +} + +void Name::init(Builtin builtin, quint32 line, quint32 column) +{ + this->id = 0; + this->builtin = builtin; + this->global = false; + this->line = line; + this->column = column; +} + +static const char *builtin_to_string(Name::Builtin b) +{ + switch (b) { + case Name::builtin_invalid: + return "builtin_invalid"; + case Name::builtin_typeof: + return "builtin_typeof"; + case Name::builtin_delete: + return "builtin_delete"; + case Name::builtin_postincrement: + return "builtin_postincrement"; + case Name::builtin_postdecrement: + return "builtin_postdecrement"; + case Name::builtin_throw: + return "builtin_throw"; + case Name::builtin_finish_try: + return "builtin_finish_try"; + case V4IR::Name::builtin_foreach_iterator_object: + return "builtin_foreach_iterator_object"; + case V4IR::Name::builtin_foreach_next_property_name: + return "builtin_foreach_next_property_name"; + case V4IR::Name::builtin_push_with_scope: + return "builtin_push_with_scope"; + case V4IR::Name::builtin_pop_scope: + return "builtin_pop_scope"; + case V4IR::Name::builtin_declare_vars: + return "builtin_declare_vars"; + case V4IR::Name::builtin_define_property: + return "builtin_define_property"; + case V4IR::Name::builtin_define_array: + return "builtin_define_array"; + case V4IR::Name::builtin_define_getter_setter: + return "builtin_define_getter_setter"; + case V4IR::Name::builtin_define_object_literal: + return "builtin_define_object_literal"; + } + return "builtin_(###FIXME)"; +}; + +void Name::dump(QTextStream &out) const +{ + if (id) + out << *id; + else + out << builtin_to_string(builtin); +} + +void Temp::dump(QTextStream &out) const +{ + out << dumpStart(this); + switch (kind) { + case Formal: out << '#' << index; break; + case ScopedFormal: out << '#' << index + << '@' << scope; break; + case Local: out << '$' << index; break; + case ScopedLocal: out << '$' << index + << '@' << scope; break; + case VirtualRegister: out << '%' << index; break; + default: out << "INVALID"; + } + out << dumpEnd(this); +} + +bool operator<(const Temp &t1, const Temp &t2) Q_DECL_NOTHROW +{ + if (t1.kind < t2.kind) return true; + if (t1.kind > t2.kind) return false; + if (t1.index < t2.index) return true; + if (t1.index > t2.index) return false; + return t1.scope < t2.scope; +} + +void Closure::dump(QTextStream &out) const +{ + QString name = value->name ? *value->name : QString(); + if (name.isEmpty()) + name.sprintf("%p", value); + out << "closure(" << name << ')'; +} + +void Convert::dump(QTextStream &out) const +{ + out << dumpStart(this); + out << "convert("; + expr->dump(out); + out << ')' << dumpEnd(this); +} + +void Unop::dump(QTextStream &out) const +{ + out << dumpStart(this) << opname(op); + expr->dump(out); + out << dumpEnd(this); +} + +void Binop::dump(QTextStream &out) const +{ + out << dumpStart(this); + left->dump(out); + out << ' ' << opname(op) << ' '; + right->dump(out); + out << dumpEnd(this); +} + +void Call::dump(QTextStream &out) const +{ + base->dump(out); + out << '('; + for (ExprList *it = args; it; it = it->next) { + if (it != args) + out << ", "; + it->expr->dump(out); + } + out << ')'; +} + +void New::dump(QTextStream &out) const +{ + out << "new "; + base->dump(out); + out << '('; + for (ExprList *it = args; it; it = it->next) { + if (it != args) + out << ", "; + it->expr->dump(out); + } + out << ')'; +} + +void Subscript::dump(QTextStream &out) const +{ + base->dump(out); + out << '['; + index->dump(out); + out << ']'; +} + +void Member::dump(QTextStream &out) const +{ + base->dump(out); + out << '.' << *name; +} + +void Exp::dump(QTextStream &out, Mode) +{ + out << "(void) "; + expr->dump(out); + out << ';'; +} + +void Move::dump(QTextStream &out, Mode) +{ + target->dump(out); + out << ' '; + if (op != OpInvalid) + out << opname(op); + out << "= "; +// if (source->type != target->type) +// out << typeName(source->type) << "_to_" << typeName(target->type) << '('; + source->dump(out); +// if (source->type != target->type) +// out << ')'; + out << ';'; +} + +void Jump::dump(QTextStream &out, Mode mode) +{ + Q_UNUSED(mode); + out << "goto " << 'L' << target->index << ';'; +} + +void CJump::dump(QTextStream &out, Mode mode) +{ + Q_UNUSED(mode); + out << "if ("; + cond->dump(out); + if (mode == HIR) + out << ") goto " << 'L' << iftrue->index << "; else goto " << 'L' << iffalse->index << ';'; + else + out << ") goto " << 'L' << iftrue->index << ";"; +} + +void Ret::dump(QTextStream &out, Mode) +{ + out << "return"; + if (expr) { + out << ' '; + expr->dump(out); + } + out << ';'; +} + +void Try::dump(QTextStream &out, Stmt::Mode mode) +{ + out << "try L" << tryBlock->index << "; catch exception in "; + exceptionVar->dump(out); + out << " with the name " << exceptionVarName << " and go to L" << catchBlock->index << ';'; +} + +void Phi::dump(QTextStream &out, Stmt::Mode mode) +{ + targetTemp->dump(out); + out << " = phi("; + for (int i = 0, ei = incoming.size(); i < ei; ++i) { + if (i > 0) + out << ", "; + if (incoming[i]) + incoming[i]->dump(out); + } + out << ");"; +} + +Function *Module::newFunction(const QString &name, Function *outer) +{ + Function *f = new Function(this, outer, name); + functions.append(f); + if (!outer) { + assert(!rootFunction); + rootFunction = f; + } else { + outer->nestedFunctions.append(f); + } + return f; +} + +Module::~Module() +{ + foreach (Function *f, functions) { + delete f; + } +} + +Function::~Function() +{ + // destroy the Stmt::Data blocks manually, because memory pool cleanup won't + // call the Stmt destructors. + foreach (V4IR::BasicBlock *b, basicBlocks) + foreach (V4IR::Stmt *s, b->statements) + s->destroyData(); + + qDeleteAll(basicBlocks); + pool = 0; + module = 0; +} + + +const QString *Function::newString(const QString &text) +{ + return &*strings.insert(text); +} + +BasicBlock *Function::newBasicBlock(BasicBlock *containingLoop, BasicBlockInsertMode mode) +{ + BasicBlock *block = new BasicBlock(this, containingLoop); + return mode == InsertBlock ? insertBasicBlock(block) : block; +} + +void Function::dump(QTextStream &out, Stmt::Mode mode) +{ + QString n = name ? *name : QString(); + if (n.isEmpty()) + n.sprintf("%p", this); + out << "function " << n << "() {" << endl; + foreach (const QString *formal, formals) + out << "\treceive " << *formal << ';' << endl; + foreach (const QString *local, locals) + out << "\tlocal " << *local << ';' << endl; + foreach (BasicBlock *bb, basicBlocks) + bb->dump(out, mode); + out << '}' << endl; +} + +void Function::removeSharedExpressions() +{ + RemoveSharedExpressions removeSharedExpressions; + removeSharedExpressions(this); +} + +int Function::indexOfArgument(const QStringRef &string) const +{ + for (int i = formals.size() - 1; i >= 0; --i) { + if (*formals.at(i) == string) + return i; + } + return -1; +} +unsigned BasicBlock::newTemp() +{ + return function->tempCount++; +} + +Temp *BasicBlock::TEMP(unsigned index) +{ + Temp *e = function->New(); + e->init(Temp::VirtualRegister, index, 0); + return e; +} + +Temp *BasicBlock::ARG(unsigned index, unsigned scope) +{ + Temp *e = function->New(); + e->init(scope ? Temp::ScopedFormal : Temp::Formal, index, scope); + return e; +} + +Temp *BasicBlock::LOCAL(unsigned index, unsigned scope) +{ + Temp *e = function->New(); + e->init(scope ? Temp::ScopedLocal : Temp::Local, index, scope); + return e; +} + +Expr *BasicBlock::CONST(Type type, double value) +{ + Const *e = function->New(); + if (type == NumberType) { + int ival = (int)value; + // +0 != -0, so we need to convert to double when negating 0 + if (ival == value && !(value == 0 && isNegative(value))) + type = SInt32Type; + else + type = DoubleType; + } + e->init(type, value); + return e; +} + +Expr *BasicBlock::STRING(const QString *value) +{ + String *e = function->New(); + e->init(value); + return e; +} + +Expr *BasicBlock::REGEXP(const QString *value, int flags) +{ + RegExp *e = function->New(); + e->init(value, flags); + return e; +} + +Name *BasicBlock::NAME(const QString &id, quint32 line, quint32 column) +{ + Name *e = function->New(); + e->init(function->newString(id), line, column); + return e; +} + +Name *BasicBlock::GLOBALNAME(const QString &id, quint32 line, quint32 column) +{ + Name *e = function->New(); + e->initGlobal(function->newString(id), line, column); + return e; +} + + +Name *BasicBlock::NAME(Name::Builtin builtin, quint32 line, quint32 column) +{ + Name *e = function->New(); + e->init(builtin, line, column); + return e; +} + +Closure *BasicBlock::CLOSURE(Function *function) +{ + Closure *clos = function->New(); + clos->init(function); + return clos; +} + +Expr *BasicBlock::CONVERT(Expr *expr, Type type) +{ + Convert *e = function->New(); + e->init(expr, type); + return e; +} + +Expr *BasicBlock::UNOP(AluOp op, Expr *expr) +{ + Unop *e = function->New(); + e->init(op, expr); + return e; +} + +Expr *BasicBlock::BINOP(AluOp op, Expr *left, Expr *right) +{ + Binop *e = function->New(); + e->init(op, left, right); + return e; +} + +Expr *BasicBlock::CALL(Expr *base, ExprList *args) +{ + Call *e = function->New(); + e->init(base, args); + int argc = 0; + for (ExprList *it = args; it; it = it->next) + ++argc; + function->maxNumberOfArguments = qMax(function->maxNumberOfArguments, argc); + return e; +} + +Expr *BasicBlock::NEW(Expr *base, ExprList *args) +{ + New *e = function->New(); + e->init(base, args); + return e; +} + +Expr *BasicBlock::SUBSCRIPT(Expr *base, Expr *index) +{ + Subscript *e = function->New(); + e->init(base, index); + return e; +} + +Expr *BasicBlock::MEMBER(Expr *base, const QString *name) +{ + Member*e = function->New(); + e->init(base, name); + return e; +} + +Stmt *BasicBlock::EXP(Expr *expr) +{ + if (isTerminated()) + return 0; + + Exp *s = function->New(); + s->init(expr); + appendStatement(s); + return s; +} + +Stmt *BasicBlock::MOVE(Expr *target, Expr *source, AluOp op) +{ + if (isTerminated()) + return 0; + + Move *s = function->New(); + s->init(target, source, op); + appendStatement(s); + return s; +} + +Stmt *BasicBlock::JUMP(BasicBlock *target) +{ + if (isTerminated()) + return 0; + + Jump *s = function->New(); + s->init(target); + appendStatement(s); + + assert(! out.contains(target)); + out.append(target); + + assert(! target->in.contains(this)); + target->in.append(this); + + return s; +} + +Stmt *BasicBlock::CJUMP(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse) +{ + if (isTerminated()) + return 0; + + if (iftrue == iffalse) { + MOVE(TEMP(newTemp()), cond); + return JUMP(iftrue); + } + + CJump *s = function->New(); + s->init(cond, iftrue, iffalse); + appendStatement(s); + + assert(! out.contains(iftrue)); + out.append(iftrue); + + assert(! iftrue->in.contains(this)); + iftrue->in.append(this); + + assert(! out.contains(iffalse)); + out.append(iffalse); + + assert(! iffalse->in.contains(this)); + iffalse->in.append(this); + + return s; +} + +Stmt *BasicBlock::RET(Temp *expr) +{ + if (isTerminated()) + return 0; + + Ret *s = function->New(); + s->init(expr); + appendStatement(s); + return s; +} + +Stmt *BasicBlock::TRY(BasicBlock *tryBlock, BasicBlock *catchBlock, const QString &exceptionVarName, Temp *exceptionVar) +{ + if (isTerminated()) + return 0; + + Try *t = function->New(); + t->init(tryBlock, catchBlock, exceptionVarName, exceptionVar); + appendStatement(t); + + assert(! out.contains(tryBlock)); + out.append(tryBlock); + + assert(! out.contains(catchBlock)); + out.append(catchBlock); + + assert(! tryBlock->in.contains(this)); + tryBlock->in.append(this); + + assert(! catchBlock->in.contains(this)); + catchBlock->in.append(this); + + return t; +} + +void BasicBlock::dump(QTextStream &out, Stmt::Mode mode) +{ + out << 'L' << index << ':' << endl; + foreach (Stmt *s, statements) { + out << '\t'; + s->dump(out, mode); + + if (s->location.isValid()) + out << " // line: " << s->location.startLine << " ; column: " << s->location.startColumn; + + out << endl; + } +} + +void BasicBlock::appendStatement(Stmt *statement) +{ + if (nextLocation.isValid()) { + statement->location = nextLocation; + nextLocation = AST::SourceLocation(); + } + statements.append(statement); +} + +CloneExpr::CloneExpr(BasicBlock *block) + : block(block), cloned(0) +{ +} + +void CloneExpr::setBasicBlock(BasicBlock *block) +{ + this->block = block; +} + +ExprList *CloneExpr::clone(ExprList *list) +{ + if (! list) + return 0; + + ExprList *clonedList = block->function->New(); + clonedList->init(clone(list->expr), clone(list->next)); + return clonedList; +} + +void CloneExpr::visitConst(Const *e) +{ + cloned = block->CONST(e->type, e->value); +} + +void CloneExpr::visitString(String *e) +{ + cloned = block->STRING(e->value); +} + +void CloneExpr::visitRegExp(RegExp *e) +{ + cloned = block->REGEXP(e->value, e->flags); +} + +void CloneExpr::visitName(Name *e) +{ + if (e->id) + cloned = block->NAME(*e->id, e->line, e->column); + else + cloned = block->NAME(e->builtin, e->line, e->column); +} + +void CloneExpr::visitTemp(Temp *e) +{ + Temp *t = block->function->New(); + t->init(e->kind, e->index, e->scope); + cloned = t; +} + +void CloneExpr::visitClosure(Closure *e) +{ + cloned = block->CLOSURE(e->value); +} + +void CloneExpr::visitConvert(Convert *e) +{ + cloned = block->CONVERT(clone(e->expr), e->type); +} + +void CloneExpr::visitUnop(Unop *e) +{ + cloned = block->UNOP(e->op, clone(e->expr)); +} + +void CloneExpr::visitBinop(Binop *e) +{ + cloned = block->BINOP(e->op, clone(e->left), clone(e->right)); +} + +void CloneExpr::visitCall(Call *e) +{ + cloned = block->CALL(clone(e->base), clone(e->args)); +} + +void CloneExpr::visitNew(New *e) +{ + cloned = block->NEW(clone(e->base), clone(e->args)); +} + +void CloneExpr::visitSubscript(Subscript *e) +{ + cloned = block->SUBSCRIPT(clone(e->base), clone(e->index)); +} + +void CloneExpr::visitMember(Member *e) +{ + cloned = block->MEMBER(clone(e->base), e->name); +} + +} // end of namespace IR +} // end of namespace QQmlJS + +QT_END_NAMESPACE diff --git a/src/qml/compiler/qv4jsir_p.h b/src/qml/compiler/qv4jsir_p.h new file mode 100644 index 0000000000..364ff532c4 --- /dev/null +++ b/src/qml/compiler/qv4jsir_p.h @@ -0,0 +1,890 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4JSIR_P_H +#define QV4JSIR_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "private/qv4global_p.h" +#include +#include + +#include +#include +#include +#include + +#ifdef CONST +#undef CONST +#endif + +QT_BEGIN_NAMESPACE + +class QTextStream; +class QQmlType; + +namespace QV4 { +struct ExecutionContext; +} + +namespace QQmlJS { + +inline bool isNegative(double d) +{ + uchar *dch = (uchar *)&d; + if (QSysInfo::ByteOrder == QSysInfo::BigEndian) + return (dch[0] & 0x80); + else + return (dch[7] & 0x80); + +} + +namespace V4IR { + +struct BasicBlock; +struct Function; +struct Module; + +struct Stmt; +struct Expr; + +// expressions +struct Const; +struct String; +struct RegExp; +struct Name; +struct Temp; +struct Closure; +struct Convert; +struct Unop; +struct Binop; +struct Call; +struct New; +struct Subscript; +struct Member; + +// statements +struct Exp; +struct Move; +struct Jump; +struct CJump; +struct Ret; +struct Try; +struct Phi; + +enum AluOp { + OpInvalid = 0, + + OpIfTrue, + OpNot, + OpUMinus, + OpUPlus, + OpCompl, + OpIncrement, + OpDecrement, + + OpBitAnd, + OpBitOr, + OpBitXor, + + OpAdd, + OpSub, + OpMul, + OpDiv, + OpMod, + + OpLShift, + OpRShift, + OpURShift, + + OpGt, + OpLt, + OpGe, + OpLe, + OpEqual, + OpNotEqual, + OpStrictEqual, + OpStrictNotEqual, + + OpInstanceof, + OpIn, + + OpAnd, + OpOr, + + LastAluOp = OpOr +}; +AluOp binaryOperator(int op); +const char *opname(V4IR::AluOp op); + +enum Type { + UnknownType = 0, + + MissingType = 1 << 0, + UndefinedType = 1 << 1, + NullType = 1 << 2, + BoolType = 1 << 3, + + SInt32Type = 1 << 4, + UInt32Type = 1 << 5, + DoubleType = 1 << 6, + NumberType = SInt32Type | UInt32Type | DoubleType, + + StringType = 1 << 7, + ObjectType = 1 << 8 +}; +QString typeName(Type t); + +struct ExprVisitor { + virtual ~ExprVisitor() {} + virtual void visitConst(Const *) = 0; + virtual void visitString(String *) = 0; + virtual void visitRegExp(RegExp *) = 0; + virtual void visitName(Name *) = 0; + virtual void visitTemp(Temp *) = 0; + virtual void visitClosure(Closure *) = 0; + virtual void visitConvert(Convert *) = 0; + virtual void visitUnop(Unop *) = 0; + virtual void visitBinop(Binop *) = 0; + virtual void visitCall(Call *) = 0; + virtual void visitNew(New *) = 0; + virtual void visitSubscript(Subscript *) = 0; + virtual void visitMember(Member *) = 0; +}; + +struct StmtVisitor { + virtual ~StmtVisitor() {} + virtual void visitExp(Exp *) = 0; + virtual void visitMove(Move *) = 0; + virtual void visitJump(Jump *) = 0; + virtual void visitCJump(CJump *) = 0; + virtual void visitRet(Ret *) = 0; + virtual void visitTry(Try *) = 0; + virtual void visitPhi(Phi *) = 0; +}; + +struct Expr { + Type type; + + Expr(): type(UnknownType) {} + virtual ~Expr() {} + virtual void accept(ExprVisitor *) = 0; + virtual bool isLValue() { return false; } + virtual Const *asConst() { return 0; } + virtual String *asString() { return 0; } + virtual RegExp *asRegExp() { return 0; } + virtual Name *asName() { return 0; } + virtual Temp *asTemp() { return 0; } + virtual Closure *asClosure() { return 0; } + virtual Convert *asConvert() { return 0; } + virtual Unop *asUnop() { return 0; } + virtual Binop *asBinop() { return 0; } + virtual Call *asCall() { return 0; } + virtual New *asNew() { return 0; } + virtual Subscript *asSubscript() { return 0; } + virtual Member *asMember() { return 0; } + virtual void dump(QTextStream &out) const = 0; +}; + +struct ExprList { + Expr *expr; + ExprList *next; + + void init(Expr *expr, ExprList *next = 0) + { + this->expr = expr; + this->next = next; + } +}; + +struct Const: Expr { + double value; + + void init(Type type, double value) + { + this->type = type; + this->value = value; + } + + virtual void accept(ExprVisitor *v) { v->visitConst(this); } + virtual Const *asConst() { return this; } + + virtual void dump(QTextStream &out) const; +}; + +struct String: Expr { + const QString *value; + + void init(const QString *value) + { + this->value = value; + } + + virtual void accept(ExprVisitor *v) { v->visitString(this); } + virtual String *asString() { return this; } + + virtual void dump(QTextStream &out) const; + static QString escape(const QString &s); +}; + +struct RegExp: Expr { + // needs to be compatible with the flags in the lexer, and in RegExpObject + enum Flags { + RegExp_Global = 0x01, + RegExp_IgnoreCase = 0x02, + RegExp_Multiline = 0x04 + }; + + const QString *value; + int flags; + + void init(const QString *value, int flags) + { + this->value = value; + this->flags = flags; + } + + virtual void accept(ExprVisitor *v) { v->visitRegExp(this); } + virtual RegExp *asRegExp() { return this; } + + virtual void dump(QTextStream &out) const; +}; + +struct Name: Expr { + enum Builtin { + builtin_invalid, + builtin_typeof, + builtin_delete, + builtin_postincrement, + builtin_postdecrement, + builtin_throw, + builtin_finish_try, + builtin_foreach_iterator_object, + builtin_foreach_next_property_name, + builtin_push_with_scope, + builtin_pop_scope, + builtin_declare_vars, + builtin_define_property, + builtin_define_array, + builtin_define_getter_setter, + builtin_define_object_literal + }; + + const QString *id; + Builtin builtin; + bool global; + quint32 line; + quint32 column; + + void initGlobal(const QString *id, quint32 line, quint32 column); + void init(const QString *id, quint32 line, quint32 column); + void init(Builtin builtin, quint32 line, quint32 column); + + virtual void accept(ExprVisitor *v) { v->visitName(this); } + virtual bool isLValue() { return true; } + virtual Name *asName() { return this; } + + virtual void dump(QTextStream &out) const; +}; + +struct Temp: Expr { + enum Kind { + Formal = 0, + ScopedFormal, + Local, + ScopedLocal, + VirtualRegister, + PhysicalRegister, + StackSlot + }; + + unsigned index; + unsigned scope : 29; // how many scopes outside the current one? + unsigned kind : 3; + + void init(unsigned kind, unsigned index, unsigned scope) + { + Q_ASSERT((kind == ScopedLocal && scope != 0) || + (kind == ScopedFormal && scope != 0) || + (scope == 0)); + + this->kind = kind; + this->index = index; + this->scope = scope; + } + + virtual void accept(ExprVisitor *v) { v->visitTemp(this); } + virtual bool isLValue() { return true; } + virtual Temp *asTemp() { return this; } + + virtual void dump(QTextStream &out) const; +}; + +inline bool operator==(const Temp &t1, const Temp &t2) Q_DECL_NOTHROW +{ return t1.index == t2.index && t1.scope == t2.scope && t1.kind == t2.kind; } + +inline uint qHash(const Temp &t, uint seed = 0) Q_DECL_NOTHROW +{ return t.index ^ (t.kind | (t.scope << 3)) ^ seed; } + +bool operator<(const Temp &t1, const Temp &t2) Q_DECL_NOTHROW; + +struct Closure: Expr { + Function *value; + + void init(Function *value) + { + this->value = value; + } + + virtual void accept(ExprVisitor *v) { v->visitClosure(this); } + virtual Closure *asClosure() { return this; } + + virtual void dump(QTextStream &out) const; +}; + +struct Convert: Expr { + Expr *expr; + + void init(Expr *expr, Type type) + { + this->expr = expr; + this->type = type; + } + + virtual void accept(ExprVisitor *v) { v->visitConvert(this); } + virtual Convert *asConvert() { return this; } + + virtual void dump(QTextStream &out) const; +}; + +struct Unop: Expr { + AluOp op; + Expr *expr; + + void init(AluOp op, Expr *expr) + { + this->op = op; + this->expr = expr; + } + + virtual void accept(ExprVisitor *v) { v->visitUnop(this); } + virtual Unop *asUnop() { return this; } + + virtual void dump(QTextStream &out) const; +}; + +struct Binop: Expr { + AluOp op; + Expr *left; // Temp or Const + Expr *right; // Temp or Const + + void init(AluOp op, Expr *left, Expr *right) + { + this->op = op; + this->left = left; + this->right = right; + } + + virtual void accept(ExprVisitor *v) { v->visitBinop(this); } + virtual Binop *asBinop() { return this; } + + virtual void dump(QTextStream &out) const; +}; + +struct Call: Expr { + Expr *base; // Name, Member, Temp + ExprList *args; // List of Temps + + void init(Expr *base, ExprList *args) + { + this->base = base; + this->args = args; + } + + Expr *onlyArgument() const { + if (args && ! args->next) + return args->expr; + return 0; + } + + virtual void accept(ExprVisitor *v) { v->visitCall(this); } + virtual Call *asCall() { return this; } + + virtual void dump(QTextStream &out) const; +}; + +struct New: Expr { + Expr *base; // Name, Member, Temp + ExprList *args; // List of Temps + + void init(Expr *base, ExprList *args) + { + this->base = base; + this->args = args; + } + + Expr *onlyArgument() const { + if (args && ! args->next) + return args->expr; + return 0; + } + + virtual void accept(ExprVisitor *v) { v->visitNew(this); } + virtual New *asNew() { return this; } + + virtual void dump(QTextStream &out) const; +}; + +struct Subscript: Expr { + Expr *base; + Expr *index; + + void init(Expr *base, Expr *index) + { + this->base = base; + this->index = index; + } + + virtual void accept(ExprVisitor *v) { v->visitSubscript(this); } + virtual bool isLValue() { return true; } + virtual Subscript *asSubscript() { return this; } + + virtual void dump(QTextStream &out) const; +}; + +struct Member: Expr { + Expr *base; + const QString *name; + + void init(Expr *base, const QString *name) + { + this->base = base; + this->name = name; + } + + virtual void accept(ExprVisitor *v) { v->visitMember(this); } + virtual bool isLValue() { return true; } + virtual Member *asMember() { return this; } + + virtual void dump(QTextStream &out) const; +}; + +struct Stmt { + enum Mode { + HIR, + MIR + }; + + struct Data { + QVector uses; + QVector defs; + QBitArray liveIn; + QBitArray liveOut; + }; + + Data *d; + int id; + AST::SourceLocation location; + + Stmt(): d(0), id(-1) {} + virtual ~Stmt() { Q_UNREACHABLE(); } + virtual Stmt *asTerminator() { return 0; } + + virtual void accept(StmtVisitor *) = 0; + virtual Exp *asExp() { return 0; } + virtual Move *asMove() { return 0; } + virtual Jump *asJump() { return 0; } + virtual CJump *asCJump() { return 0; } + virtual Ret *asRet() { return 0; } + virtual Try *asTry() { return 0; } + virtual Phi *asPhi() { return 0; } + virtual void dump(QTextStream &out, Mode mode = HIR) = 0; + + void destroyData() { + delete d; + d = 0; + } +}; + +struct Exp: Stmt { + Expr *expr; + + void init(Expr *expr) + { + this->expr = expr; + } + + virtual void accept(StmtVisitor *v) { v->visitExp(this); } + virtual Exp *asExp() { return this; } + + virtual void dump(QTextStream &out, Mode); +}; + +struct Move: Stmt { + Expr *target; // LHS - Temp, Name, Member or Subscript + Expr *source; + AluOp op; + + void init(Expr *target, Expr *source, AluOp op) + { + this->target = target; + this->source = source; + this->op = op; + } + + virtual void accept(StmtVisitor *v) { v->visitMove(this); } + virtual Move *asMove() { return this; } + + virtual void dump(QTextStream &out, Mode); +}; + +struct Jump: Stmt { + BasicBlock *target; + + void init(BasicBlock *target) + { + this->target = target; + } + + virtual Stmt *asTerminator() { return this; } + + virtual void accept(StmtVisitor *v) { v->visitJump(this); } + virtual Jump *asJump() { return this; } + + virtual void dump(QTextStream &out, Mode mode); +}; + +struct CJump: Stmt { + Expr *cond; // Temp, Binop + BasicBlock *iftrue; + BasicBlock *iffalse; + + void init(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse) + { + this->cond = cond; + this->iftrue = iftrue; + this->iffalse = iffalse; + } + + virtual Stmt *asTerminator() { return this; } + + virtual void accept(StmtVisitor *v) { v->visitCJump(this); } + virtual CJump *asCJump() { return this; } + + virtual void dump(QTextStream &out, Mode mode); +}; + +struct Ret: Stmt { + Expr *expr; + + void init(Expr *expr) + { + this->expr = expr; + } + + virtual Stmt *asTerminator() { return this; } + + virtual void accept(StmtVisitor *v) { v->visitRet(this); } + virtual Ret *asRet() { return this; } + + virtual void dump(QTextStream &out, Mode); +}; + +struct Try: Stmt { + BasicBlock *tryBlock; + BasicBlock *catchBlock; + QString exceptionVarName; + Temp *exceptionVar; // place to store the caught exception, for use when re-throwing + + void init(BasicBlock *tryBlock, BasicBlock *catchBlock, const QString &exceptionVarName, Temp *exceptionVar) + { + this->tryBlock = tryBlock; + this->catchBlock = catchBlock; + this->exceptionVarName = exceptionVarName; + this->exceptionVar = exceptionVar; + } + + virtual Stmt *asTerminator() { return this; } + + virtual void accept(StmtVisitor *v) { v->visitTry(this); } + virtual Try *asTry() { return this; } + + virtual void dump(QTextStream &out, Mode mode); +}; + +struct Phi: Stmt { + Temp *targetTemp; + QVector incoming; + + virtual void accept(StmtVisitor *v) { v->visitPhi(this); } + virtual Phi *asPhi() { return this; } + + virtual void dump(QTextStream &out, Mode mode); +}; + +struct Q_QML_EXPORT Module { + MemoryPool pool; + QVector functions; + Function *rootFunction; + + Function *newFunction(const QString &name, Function *outer); + + Module() : rootFunction(0) {} + ~Module(); +}; + +struct Function { + Module *module; + MemoryPool *pool; + const QString *name; + QVector basicBlocks; + int tempCount; + int maxNumberOfArguments; + QSet strings; + QList formals; + QList locals; + QVector nestedFunctions; + Function *outer; + + QString sourceFile; + + int insideWithOrCatch; + + uint hasDirectEval: 1; + uint usesArgumentsObject : 1; + uint isStrict: 1; + uint isNamedExpression : 1; + uint hasTry: 1; + uint hasWith: 1; + uint unused : 26; + + template _Tp *New() { return new (pool->allocate(sizeof(_Tp))) _Tp(); } + + Function(Module *module, Function *outer, const QString &name) + : module(module) + , pool(&module->pool) + , tempCount(0) + , maxNumberOfArguments(0) + , outer(outer) + , insideWithOrCatch(0) + , hasDirectEval(false) + , usesArgumentsObject(false) + , isStrict(false) + , isNamedExpression(false) + , hasTry(false) + , hasWith(false) + , unused(0) + { this->name = newString(name); } + + ~Function(); + + enum BasicBlockInsertMode { + InsertBlock, + DontInsertBlock + }; + + BasicBlock *newBasicBlock(BasicBlock *containingLoop, BasicBlockInsertMode mode = InsertBlock); + const QString *newString(const QString &text); + + void RECEIVE(const QString &name) { formals.append(newString(name)); } + void LOCAL(const QString &name) { locals.append(newString(name)); } + + inline BasicBlock *insertBasicBlock(BasicBlock *block) { basicBlocks.append(block); return block; } + + void dump(QTextStream &out, Stmt::Mode mode = Stmt::HIR); + + void removeSharedExpressions(); + + int indexOfArgument(const QStringRef &string) const; + + bool variablesCanEscape() const + { return hasDirectEval || !nestedFunctions.isEmpty(); } +}; + +struct BasicBlock { + Function *function; + QVector statements; + QVector in; + QVector out; + QBitArray liveIn; + QBitArray liveOut; + int index; + AST::SourceLocation nextLocation; + + BasicBlock(Function *function, BasicBlock *containingLoop) + : function(function) + , index(-1) + , _containingGroup(containingLoop) + , _groupStart(false) + {} + ~BasicBlock() {} + + template inline Instr i(Instr i) { statements.append(i); return i; } + + inline bool isEmpty() const { + return statements.isEmpty(); + } + + inline Stmt *terminator() const { + if (! statements.isEmpty() && statements.at(statements.size() - 1)->asTerminator() != 0) + return statements.at(statements.size() - 1); + return 0; + } + + inline bool isTerminated() const { + if (terminator() != 0) + return true; + return false; + } + + unsigned newTemp(); + + Temp *TEMP(unsigned kind); + Temp *ARG(unsigned index, unsigned scope); + Temp *LOCAL(unsigned index, unsigned scope); + + Expr *CONST(Type type, double value); + Expr *STRING(const QString *value); + Expr *REGEXP(const QString *value, int flags); + + Name *NAME(const QString &id, quint32 line, quint32 column); + Name *NAME(Name::Builtin builtin, quint32 line, quint32 column); + + Name *GLOBALNAME(const QString &id, quint32 line, quint32 column); + + Closure *CLOSURE(Function *function); + + Expr *CONVERT(Expr *expr, Type type); + Expr *UNOP(AluOp op, Expr *expr); + Expr *BINOP(AluOp op, Expr *left, Expr *right); + Expr *CALL(Expr *base, ExprList *args = 0); + Expr *NEW(Expr *base, ExprList *args = 0); + Expr *SUBSCRIPT(Expr *base, Expr *index); + Expr *MEMBER(Expr *base, const QString *name); + + Stmt *EXP(Expr *expr); + + Stmt *MOVE(Expr *target, Expr *source, AluOp op = V4IR::OpInvalid); + + Stmt *JUMP(BasicBlock *target); + Stmt *CJUMP(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse); + Stmt *RET(Temp *expr); + Stmt *TRY(BasicBlock *tryBlock, BasicBlock *catchBlock, const QString &exceptionVarName, Temp *exceptionVar); + + void dump(QTextStream &out, Stmt::Mode mode = Stmt::HIR); + + void appendStatement(Stmt *statement); + + BasicBlock *containingGroup() const + { return _containingGroup; } + + bool isGroupStart() const + { return _groupStart; } + + void markAsGroupStart() + { _groupStart = true; } + +private: + BasicBlock *_containingGroup; + bool _groupStart; +}; + +class CloneExpr: protected V4IR::ExprVisitor +{ +public: + explicit CloneExpr(V4IR::BasicBlock *block = 0); + + void setBasicBlock(V4IR::BasicBlock *block); + + template + _Expr *operator()(_Expr *expr) + { + return clone(expr); + } + + template + _Expr *clone(_Expr *expr) + { + Expr *c = expr; + qSwap(cloned, c); + expr->accept(this); + qSwap(cloned, c); + return static_cast<_Expr *>(c); + } + +protected: + V4IR::ExprList *clone(V4IR::ExprList *list); + + virtual void visitConst(Const *); + virtual void visitString(String *); + virtual void visitRegExp(RegExp *); + virtual void visitName(Name *); + virtual void visitTemp(Temp *); + virtual void visitClosure(Closure *); + virtual void visitConvert(Convert *); + virtual void visitUnop(Unop *); + virtual void visitBinop(Binop *); + virtual void visitCall(Call *); + virtual void visitNew(New *); + virtual void visitSubscript(Subscript *); + virtual void visitMember(Member *); + +private: + V4IR::BasicBlock *block; + V4IR::Expr *cloned; +}; + +} // end of namespace IR + +} // end of namespace QQmlJS + +QT_END_NAMESPACE + +#endif // QV4IR_P_H diff --git a/src/qml/compiler/qv4ssa.cpp b/src/qml/compiler/qv4ssa.cpp new file mode 100644 index 0000000000..a139ed9fff --- /dev/null +++ b/src/qml/compiler/qv4ssa.cpp @@ -0,0 +1,2122 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4ssa_p.h" +#include "qv4util_p.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONST +#undef CONST +#endif + +#define QV4_NO_LIVENESS +#undef SHOW_SSA + +QT_USE_NAMESPACE + +using namespace QQmlJS; +using namespace V4IR; + +namespace { + +QTextStream qout(stdout, QIODevice::WriteOnly); + +void showMeTheCode(Function *function) +{ + static bool showCode = !qgetenv("SHOW_CODE").isNull(); + if (showCode) { + QVector code; + QHash leader; + + foreach (BasicBlock *block, function->basicBlocks) { + if (block->statements.isEmpty()) + continue; + leader.insert(block->statements.first(), block); + foreach (Stmt *s, block->statements) { + code.append(s); + } + } + + QString name; + if (function->name && !function->name->isEmpty()) + name = *function->name; + else + name.sprintf("%p", function); + + qout << "function " << name << "("; + for (int i = 0; i < function->formals.size(); ++i) { + if (i != 0) + qout << ", "; + qout << *function->formals.at(i); + } + qout << ")" << endl + << "{" << endl; + + foreach (const QString *local, function->locals) { + qout << " var " << *local << ';' << endl; + } + + for (int i = 0; i < code.size(); ++i) { + Stmt *s = code.at(i); + + if (BasicBlock *bb = leader.value(s)) { + qout << endl; + QByteArray str; + str.append('L'); + str.append(QByteArray::number(bb->index)); + str.append(':'); + for (int i = 66 - str.length(); i; --i) + str.append(' '); + qout << str; + qout << "// predecessor blocks:"; + foreach (BasicBlock *in, bb->in) + qout << " L" << in->index; + if (bb->in.isEmpty()) + qout << "(none)"; + if (BasicBlock *container = bb->containingGroup()) + qout << "; container block: L" << container->index; + if (bb->isGroupStart()) + qout << "; group start"; + qout << endl; + } + Stmt *n = (i + 1) < code.size() ? code.at(i + 1) : 0; +// if (n && s->asJump() && s->asJump()->target == leader.value(n)) { +// continue; +// } + + QByteArray str; + QBuffer buf(&str); + buf.open(QIODevice::WriteOnly); + QTextStream out(&buf); + if (s->id > 0) + out << s->id << ": "; + s->dump(out, Stmt::MIR); + out.flush(); + + if (s->location.isValid()) + qout << " // line: " << s->location.startLine << " column: " << s->location.startColumn << endl; + +#ifndef QV4_NO_LIVENESS + for (int i = 60 - str.size(); i >= 0; --i) + str.append(' '); + + qout << " " << str; + + // if (! s->uses.isEmpty()) { + // qout << " // uses:"; + // foreach (unsigned use, s->uses) { + // qout << " %" << use; + // } + // } + + // if (! s->defs.isEmpty()) { + // qout << " // defs:"; + // foreach (unsigned def, s->defs) { + // qout << " %" << def; + // } + // } + +# if 0 + if (! s->d->liveIn.isEmpty()) { + qout << " // lives in:"; + for (int i = 0; i < s->d->liveIn.size(); ++i) { + if (s->d->liveIn.testBit(i)) + qout << " %" << i; + } + } +# else + if (! s->d->liveOut.isEmpty()) { + qout << " // lives out:"; + for (int i = 0; i < s->d->liveOut.size(); ++i) { + if (s->d->liveOut.testBit(i)) + qout << " %" << i; + } + } +# endif +#else + qout << " " << str; +#endif + + qout << endl; + + if (n && s->asCJump() /*&& s->asCJump()->iffalse != leader.value(n)*/) { + qout << " else goto L" << s->asCJump()->iffalse->index << ";" << endl; + } + } + + qout << "}" << endl + << endl; + } +} + +class DominatorTree { + int N; + QHash dfnum; + QVector vertex; + QHash parent; + QHash ancestor; + QHash best; + QHash semi; + QHash idom; + QHash samedom; + QHash > bucket; + + void DFS(BasicBlock *p, BasicBlock *n) { + if (dfnum[n] == 0) { + dfnum[n] = N; + vertex[N] = n; + parent[n] = p; + ++N; + foreach (BasicBlock *w, n->out) + DFS(n, w); + } + } + + BasicBlock *ancestorWithLowestSemi(BasicBlock *v) { + BasicBlock *a = ancestor[v]; + if (ancestor[a]) { + BasicBlock *b = ancestorWithLowestSemi(a); + ancestor[v] = ancestor[a]; + if (dfnum[semi[b]] < dfnum[semi[best[v]]]) + best[v] = b; + } + return best[v]; + } + + void link(BasicBlock *p, BasicBlock *n) { + ancestor[n] = p; + best[n] = n; + } + + void calculateIDoms(const QVector &nodes) { + Q_ASSERT(nodes.first()->in.isEmpty()); + vertex.resize(nodes.size()); + foreach (BasicBlock *n, nodes) { + dfnum[n] = 0; + semi[n] = 0; + ancestor[n] = 0; + idom[n] = 0; + samedom[n] = 0; + } + + DFS(0, nodes.first()); + Q_ASSERT(N == nodes.size()); // fails with unreachable nodes... + + for (int i = N - 1; i > 0; --i) { + BasicBlock *n = vertex[i]; + BasicBlock *p = parent[n]; + BasicBlock *s = p; + + foreach (BasicBlock *v, n->in) { + BasicBlock *ss; + if (dfnum[v] <= dfnum[n]) + ss = v; + else + ss = semi[ancestorWithLowestSemi(v)]; + if (dfnum[ss] < dfnum[s]) + s = ss; + } + semi[n] = s; + bucket[s].insert(n); + link(p, n); + foreach (BasicBlock *v, bucket[p]) { + BasicBlock *y = ancestorWithLowestSemi(v); + Q_ASSERT(semi[y] == p); + if (semi[y] == semi[v]) + idom[v] = p; + else + samedom[v] = y; + } + bucket[p].clear(); + } + for (int i = 1; i < N; ++i) { + BasicBlock *n = vertex[i]; + Q_ASSERT(ancestor[n] && ((semi[n] && dfnum[ancestor[n]] <= dfnum[semi[n]]) || semi[n] == n)); + Q_ASSERT(bucket[n].isEmpty()); + if (BasicBlock *sdn = samedom[n]) + idom[n] = idom[sdn]; + } + +#ifdef SHOW_SSA + qout << "Immediate dominators:" << endl; + foreach (BasicBlock *to, nodes) { + qout << '\t'; + if (BasicBlock *from = idom.value(to)) + qout << from->index; + else + qout << "(none)"; + qout << " -> " << to->index << endl; + } +#endif // SHOW_SSA + } + + bool dominates(BasicBlock *dominator, BasicBlock *dominated) const { + for (BasicBlock *it = dominated; it; it = idom[it]) { + if (it == dominator) + return true; + } + + return false; + } + + void computeDF(BasicBlock *n) { + if (DF.contains(n)) + return; // TODO: verify this! + + QSet S; + foreach (BasicBlock *y, n->out) + if (idom[y] != n) + S.insert(y); + + /* + * foreach child c of n in the dominator tree + * computeDF[c] + * foreach element w of DF[c] + * if n does not dominate w or if n = w + * S.insert(w) + * DF[n] = S; + */ + foreach (BasicBlock *c, children[n]) { + computeDF(c); + foreach (BasicBlock *w, DF[c]) + if (!dominates(n, w) || n == w) + S.insert(w); + } + DF[n] = S; + +#ifdef SHOW_SSA + qout << "\tDF[" << n->index << "]: {"; + QList SList = S.values(); + for (int i = 0; i < SList.size(); ++i) { + if (i > 0) + qout << ", "; + qout << SList[i]->index; + } + qout << "}" << endl; +#endif // SHOW_SSA +#ifndef QT_NO_DEBUG + foreach (BasicBlock *fBlock, S) { + Q_ASSERT(!dominates(n, fBlock) || fBlock == n); + bool hasDominatedSucc = false; + foreach (BasicBlock *succ, fBlock->in) + if (dominates(n, succ)) + hasDominatedSucc = true; + if (!hasDominatedSucc) { + qout << fBlock->index << " in DF[" << n->index << "] has no dominated predecessors" << endl; + } + Q_ASSERT(hasDominatedSucc); + } +#endif // !QT_NO_DEBUG + } + + QHash > children; + QHash > DF; + +public: + DominatorTree(const QVector &nodes) + : N(0) + { + calculateIDoms(nodes); + + // compute children of n + foreach (BasicBlock *n, nodes) + children[idom[n]].insert(n); + +#ifdef SHOW_SSA + qout << "Dominator Frontiers:" << endl; +#endif // SHOW_SSA + foreach (BasicBlock *n, nodes) + computeDF(n); + } + + QSet operator[](BasicBlock *n) const { + return DF[n]; + } + + BasicBlock *immediateDominator(BasicBlock *bb) const { + return idom[bb]; + } +}; + +class VariableCollector: public StmtVisitor, ExprVisitor { + QHash > _defsites; + QHash > A_orig; + QSet nonLocals; + QSet killed; + + BasicBlock *currentBB; + const bool variablesCanEscape; + bool isCollectable(Temp *t) const + { + switch (t->kind) { + case Temp::Formal: + case Temp::ScopedFormal: + case Temp::ScopedLocal: + return false; + case Temp::Local: + return !variablesCanEscape; + case Temp::VirtualRegister: + return true; + default: + // PhysicalRegister and StackSlot can only get inserted later. + Q_ASSERT(!"Invalid temp kind!"); + return false; + } + } + +public: + VariableCollector(Function *function) + : variablesCanEscape(function->variablesCanEscape()) + { +#ifdef SHOW_SSA + qout << "Variables collected:" << endl; +#endif // SHOW_SSA + + foreach (BasicBlock *bb, function->basicBlocks) { + currentBB = bb; + killed.clear(); + killed.reserve(bb->statements.size() / 2); + foreach (Stmt *s, bb->statements) { + s->accept(this); + } + } + +#ifdef SHOW_SSA + qout << "Non-locals:" << endl; + foreach (const Temp &nonLocal, nonLocals) { + qout << "\t"; + nonLocal.dump(qout); + qout << endl; + } + + qout << "end collected variables." << endl; +#endif // SHOW_SSA + } + + QList vars() const { + return _defsites.keys(); + } + + QSet defsite(const Temp &n) const { + return _defsites[n]; + } + + QSet inBlock(BasicBlock *n) const { + return A_orig[n]; + } + + bool isNonLocal(const Temp &var) const { return nonLocals.contains(var); } + +protected: + virtual void visitPhi(Phi *) {}; + virtual void visitConvert(Convert *e) { e->expr->accept(this); }; + + virtual void visitConst(Const *) {} + virtual void visitString(String *) {} + virtual void visitRegExp(RegExp *) {} + virtual void visitName(Name *) {} + virtual void visitClosure(Closure *) {} + virtual void visitUnop(Unop *e) { e->expr->accept(this); } + virtual void visitBinop(Binop *e) { e->left->accept(this); e->right->accept(this); } + virtual void visitSubscript(Subscript *e) { e->base->accept(this); e->index->accept(this); } + virtual void visitMember(Member *e) { e->base->accept(this); } + virtual void visitExp(Exp *s) { s->expr->accept(this); } + virtual void visitJump(Jump *) {} + virtual void visitCJump(CJump *s) { s->cond->accept(this); } + virtual void visitRet(Ret *s) { s->expr->accept(this); } + virtual void visitTry(Try *) { // ### TODO + } + + virtual void visitCall(Call *e) { + e->base->accept(this); + for (ExprList *it = e->args; it; it = it->next) + it->expr->accept(this); + } + + virtual void visitNew(New *e) { + e->base->accept(this); + for (ExprList *it = e->args; it; it = it->next) + it->expr->accept(this); + } + + virtual void visitMove(Move *s) { + s->source->accept(this); + + if (Temp *t = s->target->asTemp()) { + if (isCollectable(t)) { +#ifdef SHOW_SSA + qout << '\t'; + t->dump(qout); + qout << " -> L" << currentBB->index << endl; +#endif // SHOW_SSA + + _defsites[*t].insert(currentBB); + A_orig[currentBB].insert(*t); + + // For semi-pruned SSA: + killed.insert(*t); + } + } + } + + virtual void visitTemp(Temp *t) + { + if (isCollectable(t)) + if (!killed.contains(*t)) + nonLocals.insert(*t); + } +}; + +void insertPhiNode(const Temp &a, BasicBlock *y, Function *f) { +#if defined(SHOW_SSA) + qout << "-> inserted phi node for variable "; + a.dump(qout); + qout << " in block " << y->index << endl; +#endif + + Phi *phiNode = f->New(); + phiNode->targetTemp = f->New(); + phiNode->targetTemp->init(a.kind, a.index, 0); + y->statements.prepend(phiNode); + + phiNode->incoming.resize(y->in.size()); + for (int i = 0, ei = y->in.size(); i < ei; ++i) { + Temp *t = f->New(); + t->init(a.kind, a.index, 0); + phiNode->incoming[i] = t; + } +} + +class VariableRenamer: public StmtVisitor, public ExprVisitor +{ + Function *function; + QHash > stack; + QSet seen; + + QHash defCounts; + + const bool variablesCanEscape; + bool isRenamable(Temp *t) const + { + switch (t->kind) { + case Temp::Formal: + case Temp::ScopedFormal: + case Temp::ScopedLocal: + return false; + case Temp::Local: + return !variablesCanEscape; + case Temp::VirtualRegister: + return true; + default: + Q_ASSERT(!"Invalid temp kind!"); + return false; + } + } + int nextFreeTemp() { + const int next = function->tempCount++; +// qDebug()<<"Next free temp:"<variablesCanEscape()) + { + if (!variablesCanEscape) { + Temp t; + t.init(Temp::Local, 0, 0); + for (int i = 0, ei = f->locals.size(); i != ei; ++i) { + t.index = i; + stack[t].push(nextFreeTemp()); + } + } + + Temp t; + t.init(Temp::VirtualRegister, 0, 0); + for (int i = 0, ei = f->tempCount; i != ei; ++i) { + t.index = i; + stack[t].push(i); + } + } + + void run() { + foreach (BasicBlock *n, function->basicBlocks) + rename(n); + +#ifdef SHOW_SSA +// qout << "Temp to local mapping:" << endl; +// foreach (int key, tempMapping.keys()) +// qout << '\t' << key << " -> " << tempMapping[key] << endl; +#endif + } + + void rename(BasicBlock *n) { + if (seen.contains(n)) + return; + seen.insert(n); +// qDebug() << "I: L"<index; + + // [1]: + foreach (Stmt *s, n->statements) + s->accept(this); + + QHash dc = defCounts; + defCounts.clear(); + + // [2]: + foreach (BasicBlock *Y, n->out) { + const int j = Y->in.indexOf(n); + Q_ASSERT(j >= 0 && j < Y->in.size()); + foreach (Stmt *s, Y->statements) { + if (Phi *phi = s->asPhi()) { + Temp *t = phi->incoming[j]->asTemp(); + unsigned newTmp = stack[*t].top(); +// qDebug()<<"I: replacing phi use"<index; + t->index = newTmp; + t->kind = Temp::VirtualRegister; + } else { + break; + } + } + } + + // [3]: + foreach (BasicBlock *X, n->out) + rename(X); + + // [4]: + for (QHash::const_iterator i = dc.begin(), ei = dc.end(); i != ei; ++i) { +// qDebug()< " << i.value(); + for (unsigned j = 0, ej = i.value(); j < ej; ++j) + stack[i.key()].pop(); + } + } + +protected: + virtual void visitTemp(Temp *e) { // only called for uses, not defs + if (isRenamable(e)) { +// qDebug()<<"I: replacing use of"<index<<"with"<index].top(); + e->index = stack[*e].top(); + e->kind = Temp::VirtualRegister; + } + } + + virtual void visitMove(Move *s) { + // uses: + s->source->accept(this); + + // defs: + if (Temp *t = s->target->asTemp()) + renameTemp(t); + else + s->target->accept(this); + } + + void renameTemp(Temp *t) { + if (isRenamable(t)) { + defCounts[*t] = defCounts.value(*t, 0) + 1; + const int newIdx = nextFreeTemp(); + stack[*t].push(newIdx); +// qDebug()<<"I: replacing def of"<kind = Temp::VirtualRegister; + t->index = newIdx; + } + } + + virtual void visitConvert(Convert *e) { e->expr->accept(this); } + virtual void visitPhi(Phi *s) { renameTemp(s->targetTemp); } + + virtual void visitExp(Exp *s) { s->expr->accept(this); } + + virtual void visitJump(Jump *) {} + virtual void visitCJump(CJump *s) { s->cond->accept(this); } + virtual void visitRet(Ret *s) { s->expr->accept(this); } + virtual void visitTry(Try *s) { /* this should never happen */ } + + virtual void visitConst(Const *) {} + virtual void visitString(String *) {} + virtual void visitRegExp(RegExp *) {} + virtual void visitName(Name *) {} + virtual void visitClosure(Closure *) {} + virtual void visitUnop(Unop *e) { e->expr->accept(this); } + virtual void visitBinop(Binop *e) { e->left->accept(this); e->right->accept(this); } + virtual void visitCall(Call *e) { + e->base->accept(this); + for (ExprList *it = e->args; it; it = it->next) + it->expr->accept(this); + } + + virtual void visitNew(New *e) { + e->base->accept(this); + for (ExprList *it = e->args; it; it = it->next) + it->expr->accept(this); + } + + virtual void visitSubscript(Subscript *e) { + e->base->accept(this); + e->index->accept(this); + } + + virtual void visitMember(Member *e) { + e->base->accept(this); + } +}; + +void convertToSSA(Function *function, const DominatorTree &df) +{ +#ifdef SHOW_SSA + qout << "Converting function "; + if (function->name) + qout << *function->name; + else + qout << ""; + qout << " to SSA..." << endl; +#endif // SHOW_SSA + + // Collect all applicable variables: + VariableCollector variables(function); + + // Place phi functions: + QHash > A_phi; + foreach (Temp a, variables.vars()) { + if (!variables.isNonLocal(a)) + continue; // for semi-pruned SSA + + QList W = QList::fromSet(variables.defsite(a)); + while (!W.isEmpty()) { + BasicBlock *n = W.first(); + W.removeFirst(); + foreach (BasicBlock *y, df[n]) { + if (!A_phi[y].contains(a)) { + insertPhiNode(a, y, function); + A_phi[y].insert(a); + if (!variables.inBlock(y).contains(a)) + W.append(y); + } + } + } + } + showMeTheCode(function); + + // Rename variables: + VariableRenamer(function).run(); +} + +class DefUsesCalculator: public StmtVisitor, public ExprVisitor { +public: + struct DefUse { + DefUse() + : defStmt(0) + , blockOfStatement(0) + {} + Stmt *defStmt; + BasicBlock *blockOfStatement; + QList uses; + }; + +private: + const bool _variablesCanEscape; + QHash _defUses; + QHash > _usesPerStatement; + + BasicBlock *_block; + Stmt *_stmt; + + bool isCollectible(Temp *t) const { + switch (t->kind) { + case Temp::Formal: + case Temp::ScopedFormal: + case Temp::ScopedLocal: + return false; + case Temp::Local: + return !_variablesCanEscape; + case Temp::VirtualRegister: + return true; + default: + Q_UNREACHABLE(); + return false; + } + } + + void addUse(Temp *t) { + Q_ASSERT(t); + if (!isCollectible(t)) + return; + + _defUses[*t].uses.append(_stmt); + _usesPerStatement[_stmt].append(*t); + } + + void addDef(Temp *t) { + if (!isCollectible(t)) + return; + + Q_ASSERT(!_defUses.contains(*t) || _defUses.value(*t).defStmt == 0 || _defUses.value(*t).defStmt == _stmt); + + DefUse &defUse = _defUses[*t]; + defUse.defStmt = _stmt; + defUse.blockOfStatement = _block; + } + +public: + DefUsesCalculator(Function *function) + : _variablesCanEscape(function->variablesCanEscape()) + { + foreach (BasicBlock *bb, function->basicBlocks) { + _block = bb; + foreach (Stmt *stmt, bb->statements) { + _stmt = stmt; + stmt->accept(this); + } + } + + QMutableHashIterator it(_defUses); + while (it.hasNext()) { + it.next(); + if (!it.value().defStmt) + it.remove(); + } + } + + QList defs() const { + return _defUses.keys(); + } + + void removeDef(const Temp &var) { + _defUses.remove(var); + } + + void addUses(const Temp &variable, const QList &newUses) + { _defUses[variable].uses.append(newUses); } + + int useCount(const Temp &variable) const + { return _defUses[variable].uses.size(); } + + Stmt *defStmt(const Temp &variable) const + { return _defUses[variable].defStmt; } + + BasicBlock *defStmtBlock(const Temp &variable) const + { return _defUses[variable].blockOfStatement; } + + void removeUse(Stmt *usingStmt, const Temp &var) + { _defUses[var].uses.removeAll(usingStmt); } + + QList usedVars(Stmt *s) const + { return _usesPerStatement[s]; } + + QList uses(const Temp &var) const + { return _defUses[var].uses; } + + void dump() const + { + foreach (const Temp &var, _defUses.keys()) { + const DefUse &du = _defUses[var]; + var.dump(qout); + qout<<" -> defined in block "<index<<", statement: "; + du.defStmt->dump(qout); + qout<dump(qout);qout<expr->accept(this); } + virtual void visitJump(Jump *) {} + virtual void visitCJump(CJump *s) { s->cond->accept(this); } + virtual void visitRet(Ret *s) { s->expr->accept(this); } + virtual void visitTry(Try *) {} + + virtual void visitPhi(Phi *s) { + addDef(s->targetTemp); + foreach (Expr *e, s->incoming) + addUse(e->asTemp()); + } + + virtual void visitMove(Move *s) { + if (Temp *t = s->target->asTemp()) + addDef(t); + else + s->target->accept(this); + + s->source->accept(this); + } + + virtual void visitTemp(Temp *e) { addUse(e); } + + virtual void visitConst(Const *) {} + virtual void visitString(String *) {} + virtual void visitRegExp(RegExp *) {} + virtual void visitName(Name *) {} + virtual void visitClosure(Closure *) {} + virtual void visitConvert(Convert *e) { e->expr->accept(this); } + virtual void visitUnop(Unop *e) { e->expr->accept(this); } + virtual void visitBinop(Binop *e) { e->left->accept(this); e->right->accept(this); } + virtual void visitSubscript(Subscript *e) { e->base->accept(this); e->index->accept(this); } + virtual void visitMember(Member *e) { e->base->accept(this); } + virtual void visitCall(Call *e) { + e->base->accept(this); + for (ExprList *it = e->args; it; it = it->next) + it->expr->accept(this); + } + + virtual void visitNew(New *e) { + e->base->accept(this); + for (ExprList *it = e->args; it; it = it->next) + it->expr->accept(this); + } +}; + +bool hasPhiOnlyUses(Phi *phi, const DefUsesCalculator &defUses, QSet &collectedPhis) +{ + collectedPhis.insert(phi); + foreach (Stmt *use, defUses.uses(*phi->targetTemp)) { + if (Phi *dependentPhi = use->asPhi()) { + if (!collectedPhis.contains(dependentPhi)) { + if (!hasPhiOnlyUses(dependentPhi, defUses, collectedPhis)) + return false; + } + } else { + return false; + } + } + return true; +} + +void cleanupPhis(DefUsesCalculator &defUses) +{ + QLinkedList phis; + foreach (const Temp &def, defUses.defs()) + if (Phi *phi = defUses.defStmt(def)->asPhi()) + phis.append(phi); + + QSet toRemove; + while (!phis.isEmpty()) { + Phi *phi = phis.first(); + phis.removeFirst(); + if (toRemove.contains(phi)) + continue; + QSet collectedPhis; + if (hasPhiOnlyUses(phi, defUses, collectedPhis)) + toRemove.unite(collectedPhis); + } + + foreach (Phi *phi, toRemove) { + Temp targetVar = *phi->targetTemp; + + BasicBlock *bb = defUses.defStmtBlock(targetVar); + int idx = bb->statements.indexOf(phi); + bb->statements.remove(idx); + + foreach (const Temp &usedVar, defUses.usedVars(phi)) + defUses.removeUse(phi, usedVar); + defUses.removeDef(targetVar); + } +} + +class DeadCodeElimination: public ExprVisitor { + const bool variablesCanEscape; + DefUsesCalculator &_defUses; + QVector _worklist; + +public: + DeadCodeElimination(DefUsesCalculator &defUses, Function *function) + : variablesCanEscape(function->variablesCanEscape()) + , _defUses(defUses) + { + _worklist = QVector::fromList(_defUses.defs()); + } + + void run() { + while (!_worklist.isEmpty()) { + const Temp v = _worklist.first(); + _worklist.removeFirst(); + + if (_defUses.useCount(v) == 0) { +// qDebug()<<"-"< &stmts = _defUses.defStmtBlock(v)->statements; + int idx = stmts.indexOf(s); + if (idx != -1) + stmts.remove(idx); + foreach (const Temp &usedVar, _defUses.usedVars(s)) { + _defUses.removeUse(s, usedVar); + _worklist.append(usedVar); + } + _defUses.removeDef(v); + } + } + } + +#ifdef SHOW_SSA + qout<<"******************* After dead-code elimination:"; + _defUses.dump(); +#endif + } + +private: + bool _sideEffect; + + bool hasSideEffect(Stmt *s) { + // TODO: check if this can be moved to IR building. + _sideEffect = false; + if (Move *move = s->asMove()) { + if (Temp *t = move->target->asTemp()) { + switch (t->kind) { + case Temp::Formal: + case Temp::ScopedFormal: + case Temp::ScopedLocal: + return true; + case Temp::Local: + if (variablesCanEscape) + return true; + else + break; + case Temp::VirtualRegister: + break; + default: + Q_ASSERT(!"Invalid temp kind!"); + return true; + } + move->source->accept(this); + } else { + return true; + } + } + return _sideEffect; + } + +protected: + virtual void visitConst(Const *) {} + virtual void visitString(String *) {} + virtual void visitRegExp(RegExp *) {} + virtual void visitName(Name *e) { + // TODO: maybe we can distinguish between built-ins of which we know that they do not have + // a side-effect. + if (e->builtin == Name::builtin_invalid || (e->id && *e->id != QStringLiteral("this"))) + _sideEffect = true; + } + virtual void visitTemp(Temp *e) { + } + virtual void visitClosure(Closure *) {} + virtual void visitConvert(Convert *e) { + // we do not have type information yet, so: + _sideEffect = true; + } + + virtual void visitUnop(Unop *e) { + switch (e->op) { + case OpIncrement: + case OpDecrement: + _sideEffect = true; + break; + + default: + break; + } + + if (!_sideEffect) e->expr->accept(this); + } + virtual void visitBinop(Binop *e) { if (!_sideEffect) e->left->accept(this); if (!_sideEffect) e->right->accept(this); } + virtual void visitSubscript(Subscript *e) { + // TODO: see if we can have subscript accesses without side effect + _sideEffect = true; + } + virtual void visitMember(Member *e) { + // TODO: see if we can have member accesses without side effect + _sideEffect = true; + } + virtual void visitCall(Call *e) { + _sideEffect = true; // TODO: there are built-in functions that have no side effect. + } + virtual void visitNew(New *e) { + _sideEffect = true; // TODO: there are built-in types that have no side effect. + } +}; + +class TypeInference: public StmtVisitor, public ExprVisitor { + bool _variablesCanEscape; + const DefUsesCalculator &_defUses; + QHash _tempTypes; + QSet _worklist; + struct TypingResult { + int type; + bool fullyTyped; + + TypingResult(int type, bool fullyTyped): type(type), fullyTyped(fullyTyped) {} + explicit TypingResult(int type = UnknownType): type(type), fullyTyped(type != UnknownType) {} + }; + TypingResult _ty; + +public: + TypeInference(const DefUsesCalculator &defUses) + : _defUses(defUses) + , _ty(UnknownType) + {} + + void run(Function *function) { + _variablesCanEscape = function->variablesCanEscape(); + + // TODO: the worklist handling looks a bit inefficient... check if there is something better + _worklist.clear(); + for (int i = 0, ei = function->basicBlocks.size(); i != ei; ++i) { + BasicBlock *bb = function->basicBlocks[i]; + if (i == 0 || !bb->in.isEmpty()) + foreach (Stmt *s, bb->statements) + _worklist.insert(s); + } + + while (!_worklist.isEmpty()) { + QList worklist = _worklist.values(); + _worklist.clear(); + while (!worklist.isEmpty()) { + Stmt *s = worklist.first(); + worklist.removeFirst(); +#if defined(SHOW_SSA) + qout<<"Typing stmt ";s->dump(qout);qout<dump(qout);qout<dump(qout);qout<accept(this); + std::swap(_ty, ty); + return ty.fullyTyped; + } + + TypingResult run(Expr *e) { + TypingResult ty; + std::swap(_ty, ty); + e->accept(this); + std::swap(_ty, ty); + + if (ty.type != UnknownType) + setType(e, ty.type); + return ty; + } + + bool isAlwaysAnObject(Temp *t) { + switch (t->kind) { + case Temp::Formal: + case Temp::ScopedFormal: + case Temp::ScopedLocal: + return true; + case Temp::Local: + return _variablesCanEscape; + default: + return false; + } + } + + void setType(Expr *e, int ty) { + if (Temp *t = e->asTemp()) { +#if defined(SHOW_SSA) + qout<<"Setting type for "<< (t->scope?"scoped temp ":"temp ") <index<< " to "<type = ObjectType; + } else { + e->type = (Type) ty; + + if (_tempTypes[*t] != ty) { + _tempTypes[*t] = ty; + +#if defined(SHOW_SSA) + foreach (Stmt *s, _defUses.uses(*t)) { + qout << "Pushing back dependent stmt: "; + s->dump(qout); + qout << endl; + } +#endif + + _worklist += QSet::fromList(_defUses.uses(*t)); + } + } + } else { + e->type = (Type) ty; + } + } + +protected: + virtual void visitConst(Const *e) { _ty = TypingResult(e->type); } + virtual void visitString(String *) { _ty = TypingResult(StringType); } + virtual void visitRegExp(RegExp *) { _ty = TypingResult(ObjectType); } + virtual void visitName(Name *) { _ty = TypingResult(ObjectType); } + virtual void visitTemp(Temp *e) { + if (isAlwaysAnObject(e)) + _ty = TypingResult(ObjectType); + else + _ty = TypingResult(_tempTypes.value(*e, UnknownType)); + setType(e, _ty.type); + } + virtual void visitClosure(Closure *) { _ty = TypingResult(ObjectType); } // TODO: VERIFY THIS! + virtual void visitConvert(Convert *e) { + _ty = run(e->expr); + } + + virtual void visitUnop(Unop *e) { + _ty = run(e->expr); + switch (e->op) { + case OpUPlus: _ty.type = DoubleType; return; + case OpUMinus: _ty.type = DoubleType; return; + case OpCompl: _ty.type = SInt32Type; return; + case OpNot: _ty.type = BoolType; return; + + case OpIncrement: + case OpDecrement: + Q_ASSERT(!"Inplace operators should have been removed!"); + default: + Q_UNIMPLEMENTED(); + Q_UNREACHABLE(); + } + } + + virtual void visitBinop(Binop *e) { + TypingResult leftTy = run(e->left); + TypingResult rightTy = run(e->right); + _ty.fullyTyped = leftTy.fullyTyped && rightTy.fullyTyped; + + switch (e->op) { + case OpAdd: + if (leftTy.type & StringType || rightTy.type & StringType) + _ty.type = StringType; + else if (leftTy.type != UnknownType && rightTy.type != UnknownType) + _ty.type = DoubleType; + else + _ty.type = UnknownType; + break; + case OpSub: + _ty.type = DoubleType; + break; + + case OpMul: + case OpDiv: + case OpMod: + _ty.type = DoubleType; + break; + + case OpBitAnd: + case OpBitOr: + case OpBitXor: + case OpLShift: + case OpRShift: + _ty.type = SInt32Type; + break; + case OpURShift: + _ty.type = UInt32Type; + break; + + case OpGt: + case OpLt: + case OpGe: + case OpLe: + case OpEqual: + case OpNotEqual: + case OpStrictEqual: + case OpStrictNotEqual: + case OpAnd: + case OpOr: + case OpInstanceof: + case OpIn: + _ty.type = BoolType; + break; + + default: + Q_UNIMPLEMENTED(); + Q_UNREACHABLE(); + } + } + + virtual void visitCall(Call *e) { + _ty = run(e->base); + for (ExprList *it = e->args; it; it = it->next) + _ty.fullyTyped &= run(it->expr).fullyTyped; + _ty.type = ObjectType; + } + virtual void visitNew(New *e) { + _ty = run(e->base); + for (ExprList *it = e->args; it; it = it->next) + _ty.fullyTyped &= run(it->expr).fullyTyped; + _ty.type = ObjectType; + } + virtual void visitSubscript(Subscript *e) { + _ty.fullyTyped = run(e->base).fullyTyped && run(e->index).fullyTyped; + _ty.type = ObjectType; + } + + virtual void visitMember(Member *e) { + // TODO: for QML, try to do a static lookup + _ty = run(e->base); + _ty.type = ObjectType; + } + + virtual void visitExp(Exp *s) { _ty = run(s->expr); } + virtual void visitMove(Move *s) { + TypingResult sourceTy = run(s->source); + Q_ASSERT(s->op == OpInvalid); + if (Temp *t = s->target->asTemp()) { + setType(t, sourceTy.type); + _ty = sourceTy; + return; + } + + _ty = run(s->target); + _ty.fullyTyped &= sourceTy.fullyTyped; + } + + virtual void visitJump(Jump *) { _ty = TypingResult(MissingType); } + virtual void visitCJump(CJump *s) { _ty = run(s->cond); } + virtual void visitRet(Ret *s) { _ty = run(s->expr); } + virtual void visitTry(Try *s) { setType(s->exceptionVar, ObjectType); _ty = TypingResult(MissingType); } + virtual void visitPhi(Phi *s) { + _ty = run(s->incoming[0]); + for (int i = 1, ei = s->incoming.size(); i != ei; ++i) { + TypingResult ty = run(s->incoming[i]); + _ty.type |= ty.type; + _ty.fullyTyped &= ty.fullyTyped; + } + + // TODO: check & double check the next condition! + if (_ty.type & ObjectType || _ty.type & UndefinedType || _ty.type & NullType) + _ty.type = ObjectType; + else if (_ty.type & NumberType) + _ty.type = DoubleType; + + setType(s->targetTemp, _ty.type); + } +}; + +class TypePropagation: public StmtVisitor, public ExprVisitor { + Type _ty; + + void run(Expr *&e, Type requestedType = UnknownType) { + qSwap(_ty, requestedType); + e->accept(this); + qSwap(_ty, requestedType); + + if (requestedType != UnknownType) + if (e->type != requestedType) + if (requestedType & NumberType) { +// qDebug()<<"adding conversion from"<type)<<"to"< _conversions; + + void addConversion(Expr *&expr, Type targetType) { + _conversions.append(Conversion(&expr, targetType, _currStmt)); + } + +public: + TypePropagation() : _ty(UnknownType) {} + + void run(Function *f) { + foreach (BasicBlock *bb, f->basicBlocks) { + _conversions.clear(); + + foreach (Stmt *s, bb->statements) { + _currStmt = s; + s->accept(this); + } + + foreach (const Conversion &conversion, _conversions) { + if (conversion.stmt->asMove() && conversion.stmt->asMove()->source->asTemp()) { + *conversion.expr = bb->CONVERT(*conversion.expr, conversion.targetType); + } else { + Temp *target = bb->TEMP(bb->newTemp()); + target->type = conversion.targetType; + Expr *convert = bb->CONVERT(*conversion.expr, conversion.targetType); + Move *convCall = f->New(); + convCall->init(target, convert, OpInvalid); + + Temp *source = bb->TEMP(target->index); + source->type = conversion.targetType; + *conversion.expr = source; + + int idx = bb->statements.indexOf(conversion.stmt); + bb->statements.insert(idx, convCall); + } + } + } + } + +protected: + virtual void visitConst(Const *c) { + if (_ty & NumberType && c->type & NumberType) { + c->type = _ty; + } + } + + virtual void visitString(String *) {} + virtual void visitRegExp(RegExp *) {} + virtual void visitName(Name *) {} + virtual void visitTemp(Temp *) {} + virtual void visitClosure(Closure *) {} + virtual void visitConvert(Convert *e) { run(e->expr, e->type); } + virtual void visitUnop(Unop *e) { run(e->expr, e->type); } + virtual void visitBinop(Binop *e) { + // FIXME: This routine needs more tuning! + switch (e->op) { + case OpAdd: + case OpSub: + case OpMul: + case OpDiv: + case OpMod: + case OpBitAnd: + case OpBitOr: + case OpBitXor: + case OpLShift: + case OpRShift: + case OpURShift: + run(e->left, e->type); + run(e->right, e->type); + break; + + case OpGt: + case OpLt: + case OpGe: + case OpLe: + if (e->left->type == DoubleType) + run(e->right, DoubleType); + else if (e->right->type == DoubleType) + run(e->left, DoubleType); + else { + run(e->left, e->type); + run(e->right, e->type); + } + break; + + case OpEqual: + case OpNotEqual: + case OpStrictEqual: + case OpStrictNotEqual: + break; + + case OpInstanceof: + case OpIn: + run(e->left, e->type); + run(e->right, e->type); + break; + + default: + Q_UNIMPLEMENTED(); + Q_UNREACHABLE(); + } + } + virtual void visitCall(Call *e) { + run(e->base); + for (ExprList *it = e->args; it; it = it->next) + run(it->expr); + } + virtual void visitNew(New *e) { + run(e->base); + for (ExprList *it = e->args; it; it = it->next) + run(it->expr); + } + virtual void visitSubscript(Subscript *e) { run(e->base); run(e->index); } + virtual void visitMember(Member *e) { run(e->base); } + virtual void visitExp(Exp *s) { run(s->expr); } + virtual void visitMove(Move *s) { + run(s->target); + run(s->source, s->target->type); + } + virtual void visitJump(Jump *) {} + virtual void visitCJump(CJump *s) { + run(s->cond, BoolType); + } + virtual void visitRet(Ret *s) { run(s->expr); } + virtual void visitTry(Try *) {} + virtual void visitPhi(Phi *s) { + Type ty = s->targetTemp->type; + foreach (Expr *e, s->incoming) + if (e->asConst()) + run(e, ty); + } +}; + +void doEdgeSplitting(Function *f) +{ + const QVector oldBBs = f->basicBlocks; + + foreach (BasicBlock *bb, oldBBs) { + if (bb->in.size() > 1) { + for (int inIdx = 0, eInIdx = bb->in.size(); inIdx != eInIdx; ++inIdx) { + BasicBlock *inBB = bb->in[inIdx]; + if (inBB->out.size() > 1) { // this should have been split! +#if defined(SHOW_SSA) + qDebug() << "Splitting edge from block" << inBB->index << "to block" << bb->index; +#endif + + // create the basic block: + BasicBlock *newBB = new BasicBlock(f, bb->containingGroup()); + newBB->index = f->basicBlocks.last()->index + 1; + f->basicBlocks.append(newBB); + Jump *s = f->New(); + s->init(bb); + newBB->statements.append(s); + + // rewire the old outgoing edge + int outIdx = inBB->out.indexOf(bb); + inBB->out[outIdx] = newBB; + newBB->in.append(inBB); + + // rewire the old incoming edge + bb->in[inIdx] = newBB; + newBB->out.append(bb); + + // patch the terminator + Stmt *terminator = inBB->terminator(); + if (Jump *j = terminator->asJump()) { + Q_ASSERT(outIdx == 0); + j->target = newBB; + } else if (CJump *j = terminator->asCJump()) { + if (outIdx == 0) + j->iftrue = newBB; + else if (outIdx == 1) + j->iffalse = newBB; + else + Q_ASSERT(!"Invalid out edge index for CJUMP!"); + } else { + Q_ASSERT(!"Unknown terminator!"); + } + } + } + } + } +} + +QHash scheduleBlocks(Function *function, const DominatorTree &df) +{ + struct I { + const DominatorTree &df; + QHash &startEndLoops; + QSet visited; + QVector &sequence; + BasicBlock *currentGroup; + QList postponed; + + I(const DominatorTree &df, QVector &sequence, + QHash &startEndLoops) + : df(df) + , sequence(sequence) + , startEndLoops(startEndLoops) + , currentGroup(0) + {} + + void DFS(BasicBlock *bb) { + Q_ASSERT(bb); + if (visited.contains(bb)) + return; + + if (bb->containingGroup() != currentGroup) { + postponed.append(bb); + return; + } + if (bb->isGroupStart()) + currentGroup = bb; + else if (bb->in.size() > 1) + foreach (BasicBlock *inBB, bb->in) + if (!visited.contains(inBB)) + return; + + Q_ASSERT(df.immediateDominator(bb) == 0 || sequence.contains(df.immediateDominator(bb))); + layout(bb); + if (Stmt *terminator = bb->terminator()) { + if (Jump *j = terminator->asJump()) { + Q_ASSERT(bb->out.size() == 1); + DFS(j->target); + } else if (CJump *cj = terminator->asCJump()) { + Q_ASSERT(bb->out.size() == 2); + DFS(cj->iftrue); + DFS(cj->iffalse); + } else if (terminator->asRet()) { + Q_ASSERT(bb->out.size() == 0); + // nothing to do. + } else { + Q_UNREACHABLE(); + } + } else { + Q_UNREACHABLE(); + } + + if (bb->isGroupStart()) { + currentGroup = bb->containingGroup(); + startEndLoops.insert(bb, sequence.last()); + QList p = postponed; + foreach (BasicBlock *pBB, p) + DFS(pBB); + } + } + + void layout(BasicBlock *bb) { + sequence.append(bb); + visited.insert(bb); + postponed.removeAll(bb); + } + }; + + QVector sequence; + sequence.reserve(function->basicBlocks.size()); + QHash startEndLoops; + I(df, sequence, startEndLoops).DFS(function->basicBlocks.first()); + qSwap(function->basicBlocks, sequence); + + showMeTheCode(function); + return startEndLoops; +} + +void checkCriticalEdges(QVector basicBlocks) { + foreach (BasicBlock *bb, basicBlocks) { + if (bb && bb->out.size() > 1) { + foreach (BasicBlock *bb2, bb->out) { + if (bb2 && bb2->in.size() > 1) { + qout << "found critical edge between block " + << bb->index << " and block " << bb2->index; + Q_ASSERT(false); + } + } + } + } +} + +void cleanupBasicBlocks(Function *function) +{ +// showMeTheCode(function); + + // remove all basic blocks that have no incoming edges, but skip the entry block + QVector W = function->basicBlocks; + W.removeFirst(); + QSet toRemove; + + while (!W.isEmpty()) { + BasicBlock *bb = W.first(); + W.removeFirst(); + if (toRemove.contains(bb)) + continue; + if (bb->in.isEmpty()) { + foreach (BasicBlock *outBB, bb->out) { + int idx = outBB->in.indexOf(bb); + if (idx != -1) { + outBB->in.remove(idx); + W.append(outBB); + } + } + toRemove.insert(bb); + } + } + + // TODO: merge 2 basic blocks A and B if A has one outgoing edge (to B), B has one incoming + // edge (from A), but not when A has more than 1 incoming edge and B has more than one + // outgoing edge. + + foreach (BasicBlock *bb, toRemove) { + foreach (Stmt *s, bb->statements) + s->destroyData(); + int idx = function->basicBlocks.indexOf(bb); + if (idx != -1) + function->basicBlocks.remove(idx); + delete bb; + } + + // re-number all basic blocks: + for (int i = 0; i < function->basicBlocks.size(); ++i) + function->basicBlocks[i]->index = i; +} + +class InputOutputCollector: protected StmtVisitor, protected ExprVisitor { + const bool variablesCanEscape; + +public: + QList inputs; + QList outputs; + + InputOutputCollector(bool variablesCanEscape): variablesCanEscape(variablesCanEscape) {} + + void collect(Stmt *s) { + inputs.clear(); + outputs.clear(); + s->accept(this); + } + +protected: + virtual void visitConst(Const *) {} + virtual void visitString(String *) {} + virtual void visitRegExp(RegExp *) {} + virtual void visitName(Name *) {} + virtual void visitTemp(Temp *e) { + switch (e->kind) { + case Temp::Local: + if (!variablesCanEscape) + inputs.append(*e); + break; + + case Temp::VirtualRegister: + inputs.append(*e); + break; + + default: + break; + } + } + virtual void visitClosure(Closure *) {} + virtual void visitConvert(Convert *e) { e->expr->accept(this); } + virtual void visitUnop(Unop *e) { e->expr->accept(this); } + virtual void visitBinop(Binop *e) { e->left->accept(this); e->right->accept(this); } + virtual void visitCall(Call *e) { + e->base->accept(this); + for (ExprList *it = e->args; it; it = it->next) + it->expr->accept(this); + } + virtual void visitNew(New *e) { + e->base->accept(this); + for (ExprList *it = e->args; it; it = it->next) + it->expr->accept(this); + } + virtual void visitSubscript(Subscript *e) { e->base->accept(this); e->index->accept(this); } + virtual void visitMember(Member *e) { e->base->accept(this); } + virtual void visitExp(Exp *s) { s->expr->accept(this); } + virtual void visitMove(Move *s) { + s->source->accept(this); + if (Temp *t = s->target->asTemp()) { + if ((t->kind == Temp::Local && !variablesCanEscape) || t->kind == Temp::VirtualRegister) + outputs.append(*t); + else + s->target->accept(this); + } else { + s->target->accept(this); + } + } + virtual void visitJump(Jump *) {} + virtual void visitCJump(CJump *s) { s->cond->accept(this); } + virtual void visitRet(Ret *s) { s->expr->accept(this); } + virtual void visitTry(Try *) {} + virtual void visitPhi(Phi *s) { + // Handled separately + } +}; + +/* + * The algorithm is described in: + * + * Linear Scan Register Allocation on SSA Form + * Christian Wimmer & Michael Franz, CGO'10, April 24-28, 2010 + * + * There is one slight difference w.r.t. the phi-nodes: in the artice, the phi nodes are attached + * to the basic-blocks. Therefore, in the algorithm in the article, the ranges for input parameters + * for phi nodes run from their definition upto the branch instruction into the block with the phi + * node. In our representation, phi nodes are mostly treaded as normal instructions, so we have to + * enlarge the range to cover the phi node itself. + */ +class LifeRanges { + typedef QSet LiveRegs; + + QHash _liveIn; + QHash _intervals; + QList _sortedRanges; + +public: + LifeRanges(Function *function, const QHash &startEndLoops) + { + int id = 0; + foreach (BasicBlock *bb, function->basicBlocks) { + foreach (Stmt *s, bb->statements) { + if (s->asPhi()) + s->id = id + 1; + else + s->id = ++id; + } + } + + for (int i = function->basicBlocks.size() - 1; i >= 0; --i) { + BasicBlock *bb = function->basicBlocks[i]; + buildIntervals(bb, startEndLoops.value(bb, 0), function->variablesCanEscape()); + } + + _sortedRanges.reserve(_intervals.size()); + for (QHash::const_iterator i = _intervals.begin(), ei = _intervals.end(); i != ei; ++i) { + LifeTimeInterval range = i.value(); + range.setTemp(i.key()); + _sortedRanges.append(range); + } + qSort(_sortedRanges.begin(), _sortedRanges.end(), LifeTimeInterval::lessThan); + } + + QList ranges() const { return _sortedRanges; } + + void dump() const + { + qout << "Life ranges:" << endl; + qout << "Intervals:" << endl; + foreach (const LifeTimeInterval &range, _sortedRanges) { + range.dump(); + qout << endl; + } + + foreach (BasicBlock *bb, _liveIn.keys()) { + qout << "L" << bb->index <<" live-in: "; + QList live = QList::fromSet(_liveIn.value(bb)); + qSort(live); + for (int i = 0; i < live.size(); ++i) { + if (i > 0) qout << ", "; + live[i].dump(qout); + } + qout << endl; + } + } + +private: + void buildIntervals(BasicBlock *bb, BasicBlock *loopEnd, bool variablesCanEscape) + { + LiveRegs live; + foreach (BasicBlock *successor, bb->out) { + live.unite(_liveIn[successor]); + const int bbIndex = successor->in.indexOf(bb); + Q_ASSERT(bbIndex >= 0); + + foreach (Stmt *s, successor->statements) { + if (Phi *phi = s->asPhi()) { + if (Temp *t = phi->incoming[bbIndex]->asTemp()) + live.insert(*t); + } else { + break; + } + } + } + + foreach (const Temp &opd, live) + _intervals[opd].addRange(bb->statements.first(), bb->statements.last()); + + InputOutputCollector collector(variablesCanEscape); + for (int i = bb->statements.size() - 1; i >= 0; --i) { + Stmt *s = bb->statements[i]; + if (Phi *phi = s->asPhi()) { + live.remove(*phi->targetTemp); + continue; + } + collector.collect(s); + foreach (const Temp &opd, collector.outputs) { + _intervals[opd].setFrom(s); + live.remove(opd); + } + foreach (const Temp &opd, collector.inputs) { + _intervals[opd].addRange(bb->statements.first(), s); + live.insert(opd); + } + } + + if (loopEnd) { // Meaning: bb is a loop header, because loopEnd is set to non-null. + foreach (const Temp &opd, live) + _intervals[opd].addRange(bb->statements.first(), loopEnd->statements.last()); + } + + _liveIn[bb] = live; + } +}; +} // anonymous namespace + +void LifeTimeInterval::setFrom(Stmt *from) { + Q_ASSERT(from && from->id > 0); + + if (_ranges.isEmpty()) // this is the case where there is no use, only a define + _ranges.push_front(Range(from->id, from->id)); + else + _ranges.first().start = from->id; +} + +void LifeTimeInterval::addRange(Stmt *from, Stmt *to) { + Q_ASSERT(from && from->id > 0); + Q_ASSERT(to && to->id > 0); + Q_ASSERT(to->id >= from->id); + + if (_ranges.isEmpty()) { + _ranges.push_front(Range(from->id, to->id)); + return; + } + + Range *p = &_ranges.first(); + if (to->id + 1 >= p->start && p->end + 1 >= from->id) { + p->start = qMin(p->start, from->id); + p->end = qMax(p->end, to->id); + while (_ranges.count() > 1) { + Range *p1 = &_ranges[1]; + if (p->end + 1 < p1->start || p1->end + 1 < p->start) + break; + p1->start = qMin(p->start, p1->start); + p1->end = qMax(p->end, p1->end); + _ranges.pop_front(); + p = &_ranges.first(); + } + } else { + Q_ASSERT(to->id < p->start); + _ranges.push_front(Range(from->id, to->id)); + } +} + +void LifeTimeInterval::dump() const { + _temp.dump(qout); + qout << ": "; + if (_ranges.isEmpty()) + qout << "(none)"; + for (int i = 0; i < _ranges.size(); ++i) { + if (i > 0) qout << ", "; + qout << _ranges[i].start << " - " << _ranges[i].end; + } + if (_reg != Invalid) + qout << " (register " << _reg << ")"; +} + +bool LifeTimeInterval::lessThan(const LifeTimeInterval &r1, const LifeTimeInterval &r2) { + if (r1._ranges.first().start == r2._ranges.first().start) + return r1._ranges.last().end < r2._ranges.last().end; + else + return r1._ranges.first().start < r2._ranges.first().start; +} + +void Optimizer::run() +{ +#if defined(SHOW_SSA) + qout << "##### NOW IN FUNCTION " << (_function->name ? qPrintable(*_function->name) : "anonymous!") + << " with " << _function->basicBlocks.size() << " basic blocks." << endl << flush; +#endif + + // Number all basic blocks, so we have nice numbers in the dumps: + for (int i = 0; i < function->basicBlocks.size(); ++i) + function->basicBlocks[i]->index = i; + showMeTheCode(function); + + cleanupBasicBlocks(function); + + function->removeSharedExpressions(); + +// showMeTheCode(function); + + if (!function->hasTry && !function->hasWith) { +// qout << "Starting edge splitting..." << endl; + doEdgeSplitting(function); +// showMeTheCode(function); + + // Calculate the dominator tree: + DominatorTree df(function->basicBlocks); + + convertToSSA(function, df); +// showMeTheCode(function); + +// qout << "Starting def/uses calculation..." << endl; + DefUsesCalculator defUses(function); + +// qout << "Cleaning up phi nodes..." << endl; + cleanupPhis(defUses); +// showMeTheCode(function); + +// qout << "Starting dead-code elimination..." << endl; + DeadCodeElimination(defUses, function).run(); +// showMeTheCode(function); + +// qout << "Running type inference..." << endl; + TypeInference(defUses).run(function); +// showMeTheCode(function); + +// qout << "Doing type propagation..." << endl; + TypePropagation().run(function); +// showMeTheCode(function); + +// qout << "Doing block scheduling..." << endl; + startEndLoops = scheduleBlocks(function, df); +// showMeTheCode(function); + +#ifndef QT_NO_DEBUG + checkCriticalEdges(function->basicBlocks); +#endif + +// qout << "Finished." << endl; + inSSA = true; + } else { + inSSA = false; + } +} + +namespace { +void insertMove(Function *function, BasicBlock *basicBlock, Temp *target, Expr *source) { + if (target->type != source->type) + source = basicBlock->CONVERT(source, target->type); + + Move *s = function->New(); + s->init(target, source, OpInvalid); + basicBlock->statements.insert(basicBlock->statements.size() - 1, s); +} +} + +/* + * Quick function to convert out of SSA, so we can put the stuff through the ISel phases. This + * has to be replaced by a phase in the specific ISel back-ends and do register allocation at the + * same time. That way the huge number of redundant moves generated by this function are eliminated. + */ +void Optimizer::convertOutOfSSA() { + // We assume that edge-splitting is already done. + foreach (BasicBlock *bb, function->basicBlocks) { + QVector &stmts = bb->statements; + while (!stmts.isEmpty()) { + Stmt *s = stmts.first(); + if (Phi *phi = s->asPhi()) { + stmts.removeFirst(); + for (int i = 0, ei = phi->incoming.size(); i != ei; ++i) + insertMove(function, bb->in[i], phi->targetTemp, phi->incoming[i]); + } else { + break; + } + } + } +} + +QList Optimizer::ssaDeconstructionMoves(BasicBlock *basicBlock) +{ + QList moves; + + foreach (BasicBlock *outEdge, basicBlock->out) { + int inIdx = outEdge->in.indexOf(basicBlock); + Q_ASSERT(inIdx >= 0); + foreach (Stmt *s, outEdge->statements) { + if (Phi *phi = s->asPhi()) { + SSADeconstructionMove m; + m.source = phi->incoming[inIdx]; + m.target = phi->targetTemp; + moves.append(m); + } else { + break; + } + } + } + + return moves; +} + +QList Optimizer::lifeRanges() const +{ + Q_ASSERT(isInSSA()); + + LifeRanges lifeRanges(function, startEndLoops); +// lifeRanges.dump(); +// showMeTheCode(function); + return lifeRanges.ranges(); +} diff --git a/src/qml/compiler/qv4ssa_p.h b/src/qml/compiler/qv4ssa_p.h new file mode 100644 index 0000000000..097a40eff1 --- /dev/null +++ b/src/qml/compiler/qv4ssa_p.h @@ -0,0 +1,127 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4SSA_P_H +#define QV4SSA_P_H + +#include "qv4jsir_p.h" + +QT_BEGIN_NAMESPACE +namespace QQmlJS { +namespace V4IR { + +class LifeTimeInterval { + struct Range { + int start; + int end; + + Range(int start = Invalid, int end = Invalid) + : start(start) + , end(end) + {} + }; + + Temp _temp; + QList _ranges; + int _reg; + +public: + static const int Invalid = -1; + + LifeTimeInterval() + : _reg(Invalid) + {} + + void setTemp(const Temp &temp) { this->_temp = temp; } + Temp temp() const { return _temp; } + + void setFrom(Stmt *from); + void addRange(Stmt *from, Stmt *to); + + int start() const { return _ranges.first().start; } + int end() const { return _ranges.last().end; } + + int reg() const { return _reg; } + void setReg(int reg) { _reg = reg; } + + void dump() const; + static bool lessThan(const LifeTimeInterval &r1, const LifeTimeInterval &r2); +}; + +class Optimizer +{ +public: + struct SSADeconstructionMove + { + Expr *source; + Temp *target; + + bool needsConversion() const + { return target->type != source->type; } + }; + +public: + Optimizer(Function *function) + : function(function) + , inSSA(false) + {} + + void run(); + void convertOutOfSSA(); + + bool isInSSA() const + { return inSSA; } + + QList ssaDeconstructionMoves(BasicBlock *basicBlock); + + QList lifeRanges() const; + +private: + Function *function; + bool inSSA; + QHash startEndLoops; +}; + +} // V4IR namespace +} // QQmlJS namespace +QT_END_NAMESPACE + +#endif // QV4SSA_P_H diff --git a/src/qml/compiler/qv4vme_moth.cpp b/src/qml/compiler/qv4vme_moth.cpp new file mode 100644 index 0000000000..a06ce4139a --- /dev/null +++ b/src/qml/compiler/qv4vme_moth.cpp @@ -0,0 +1,596 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4vme_moth_p.h" +#include "qv4instr_moth_p.h" +#include +#include +#include +#include + +#include + +#include "qv4alloca_p.h" + +#ifdef DO_TRACE_INSTR +# define TRACE_INSTR(I) fprintf(stderr, "executing a %s\n", #I); +# define TRACE(n, str, ...) { char buf[4096]; snprintf(buf, 4096, str, __VA_ARGS__); fprintf(stderr, " %s : %s\n", #n, buf); } +#else +# define TRACE_INSTR(I) +# define TRACE(n, str, ...) +#endif // DO_TRACE_INSTR + +using namespace QQmlJS; +using namespace QQmlJS::Moth; + +#define MOTH_BEGIN_INSTR_COMMON(I) { \ + const InstrMeta<(int)Instr::I>::DataType &instr = InstrMeta<(int)Instr::I>::data(*genericInstr); \ + code += InstrMeta<(int)Instr::I>::Size; \ + if (context->engine->debugger && (instr.breakPoint || context->engine->debugger->pauseAtNextOpportunity())) \ + context->engine->debugger->maybeBreakAtInstruction(code, instr.breakPoint); \ + Q_UNUSED(instr); \ + TRACE_INSTR(I) + +#ifdef MOTH_THREADED_INTERPRETER + +# define MOTH_BEGIN_INSTR(I) op_##I: \ + MOTH_BEGIN_INSTR_COMMON(I) + +# define MOTH_NEXT_INSTR(I) { \ + genericInstr = reinterpret_cast(code); \ + goto *genericInstr->common.code; \ + } + +# define MOTH_END_INSTR(I) } \ + genericInstr = reinterpret_cast(code); \ + goto *genericInstr->common.code; \ + +#else + +# define MOTH_BEGIN_INSTR(I) \ + case Instr::I: \ + MOTH_BEGIN_INSTR_COMMON(I) + +# define MOTH_NEXT_INSTR(I) { \ + break; \ + } + +# define MOTH_END_INSTR(I) } \ + break; + +#endif + +#ifdef WITH_STATS +namespace { +struct VMStats { + quint64 paramIsValue; + quint64 paramIsArg; + quint64 paramIsLocal; + quint64 paramIsTemp; + quint64 paramIsScopedLocal; + + VMStats() + : paramIsValue(0) + , paramIsArg(0) + , paramIsLocal(0) + , paramIsTemp(0) + , paramIsScopedLocal(0) + {} + + ~VMStats() + { show(); } + + void show() { + fprintf(stderr, "VM stats:\n"); + fprintf(stderr, " value: %lu\n", paramIsValue); + fprintf(stderr, " arg: %lu\n", paramIsArg); + fprintf(stderr, " local: %lu\n", paramIsLocal); + fprintf(stderr, " temp: %lu\n", paramIsTemp); + fprintf(stderr, " scoped local: %lu\n", paramIsScopedLocal); + } +}; +static VMStats vmStats; +#define VMSTATS(what) ++vmStats.what +} +#else // !WITH_STATS +#define VMSTATS(what) {} +#endif // WITH_STATS + +static inline QV4::Value *getValueRef(QV4::ExecutionContext *context, + QV4::Value* stack, + const Instr::Param ¶m +#if !defined(QT_NO_DEBUG) + , unsigned stackSize +#endif + ) +{ +#ifdef DO_TRACE_INSTR + if (param.isValue()) { + fprintf(stderr, " value %s\n", param.value.toString(context)->toQString().toUtf8().constData()); + } else if (param.isArgument()) { + fprintf(stderr, " argument %d@%d\n", param.index, param.scope); + } else if (param.isLocal()) { + fprintf(stderr, " local %d\n", param.index); + } else if (param.isTemp()) { + fprintf(stderr, " temp %d\n", param.index); + } else if (param.isScopedLocal()) { + fprintf(stderr, " temp %d@%d\n", param.index, param.scope); + } else { + Q_ASSERT(!"INVALID"); + } +#endif // DO_TRACE_INSTR + + if (param.isValue()) { + VMSTATS(paramIsValue); + return const_cast(¶m.value); + } else if (param.isArgument()) { + VMSTATS(paramIsArg); + QV4::ExecutionContext *c = context; + uint scope = param.scope; + while (scope--) + c = c->outer; + QV4::CallContext *cc = static_cast(c); + const unsigned arg = param.index; + Q_ASSERT(arg >= 0); + Q_ASSERT((unsigned) arg < cc->argumentCount); + Q_ASSERT(cc->arguments); + return cc->arguments + arg; + } else if (param.isLocal()) { + VMSTATS(paramIsLocal); + const unsigned index = param.index; + QV4::CallContext *c = static_cast(context); + Q_ASSERT(index >= 0); + Q_ASSERT(index < context->variableCount()); + Q_ASSERT(c->locals); + return c->locals + index; + } else if (param.isTemp()) { + VMSTATS(paramIsTemp); + Q_ASSERT(param.index < stackSize); + return stack + param.index; + } else if (param.isScopedLocal()) { + VMSTATS(paramIsScopedLocal); + QV4::ExecutionContext *c = context; + uint scope = param.scope; + while (scope--) + c = c->outer; + const unsigned index = param.index; + QV4::CallContext *cc = static_cast(c); + Q_ASSERT(index >= 0); + Q_ASSERT(index < cc->variableCount()); + Q_ASSERT(cc->locals); + return cc->locals + index; + } else { + Q_UNIMPLEMENTED(); + return 0; + } +} + +#if defined(QT_NO_DEBUG) +# define VALUE(param) (*VALUEPTR(param)) + +// The non-temp case might need some tweaking for QML: there it would probably be a value instead of a local. +# define VALUEPTR(param) \ + (param.isTemp() ? stack + param.index \ + : (param.isLocal() ? static_cast(context)->locals + param.index \ + : getValueRef(context, stack, param))) +#else +# define VALUE(param) *getValueRef(context, stack, param, stackSize) +# define VALUEPTR(param) getValueRef(context, stack, param, stackSize) +#endif + +QV4::Value VME::run(QV4::ExecutionContext *context, const uchar *&code, + QV4::Value *stack, unsigned stackSize +#ifdef MOTH_THREADED_INTERPRETER + , void ***storeJumpTable +#endif + ) +{ +#ifdef DO_TRACE_INSTR + qDebug("Starting VME with context=%p and code=%p", context, code); +#endif // DO_TRACE_INSTR + +#ifdef MOTH_THREADED_INTERPRETER + if (storeJumpTable) { +#define MOTH_INSTR_ADDR(I, FMT) &&op_##I, + static void *jumpTable[] = { + FOR_EACH_MOTH_INSTR(MOTH_INSTR_ADDR) + }; +#undef MOTH_INSTR_ADDR + *storeJumpTable = jumpTable; + return QV4::Value::undefinedValue(); + } +#endif + + context->interpreterInstructionPointer = &code; + +#ifdef MOTH_THREADED_INTERPRETER + const Instr *genericInstr = reinterpret_cast(code); + goto *genericInstr->common.code; +#else + for (;;) { + const Instr *genericInstr = reinterpret_cast(code); + switch (genericInstr->common.instructionType) { +#endif + + MOTH_BEGIN_INSTR(MoveTemp) + VALUE(instr.result) = VALUE(instr.source); + MOTH_END_INSTR(MoveTemp) + + MOTH_BEGIN_INSTR(LoadValue) +// TRACE(value, "%s", instr.value.toString(context)->toQString().toUtf8().constData()); + VALUE(instr.result) = VALUE(instr.value); + MOTH_END_INSTR(LoadValue) + + MOTH_BEGIN_INSTR(LoadClosure) + __qmljs_init_closure(context, VALUEPTR(instr.result), instr.value); + MOTH_END_INSTR(LoadClosure) + + MOTH_BEGIN_INSTR(LoadName) + TRACE(inline, "property name = %s", instr.name->toQString().toUtf8().constData()); + __qmljs_get_activation_property(context, VALUEPTR(instr.result), instr.name); + MOTH_END_INSTR(LoadName) + + MOTH_BEGIN_INSTR(StoreName) + TRACE(inline, "property name = %s", instr.name->toQString().toUtf8().constData()); + __qmljs_set_activation_property(context, instr.name, VALUE(instr.source)); + MOTH_END_INSTR(StoreName) + + MOTH_BEGIN_INSTR(LoadElement) + __qmljs_get_element(context, VALUEPTR(instr.result), VALUE(instr.base), VALUE(instr.index)); + MOTH_END_INSTR(LoadElement) + + MOTH_BEGIN_INSTR(StoreElement) + __qmljs_set_element(context, VALUE(instr.base), VALUE(instr.index), VALUE(instr.source)); + MOTH_END_INSTR(StoreElement) + + MOTH_BEGIN_INSTR(LoadProperty) + __qmljs_get_property(context, VALUEPTR(instr.result), VALUE(instr.base), instr.name); + MOTH_END_INSTR(LoadProperty) + + MOTH_BEGIN_INSTR(StoreProperty) + __qmljs_set_property(context, VALUE(instr.base), instr.name, VALUE(instr.source)); + MOTH_END_INSTR(StoreProperty) + + MOTH_BEGIN_INSTR(Push) + TRACE(inline, "stack size: %u", instr.value); + stackSize = instr.value; + stack = static_cast(alloca(stackSize * sizeof(QV4::Value))); + memset(stack, 0, stackSize * sizeof(QV4::Value)); + MOTH_END_INSTR(Push) + + MOTH_BEGIN_INSTR(CallValue) +#ifdef DO_TRACE_INSTR + if (Debugging::Debugger *debugger = context->engine->debugger) { + if (QV4::FunctionObject *o = (VALUE(instr.dest)).asFunctionObject()) { + if (Debugging::FunctionDebugInfo *info = debugger->debugInfo(o)) { + QString n = debugger->name(o); + std::cerr << "*** Call to \"" << (n.isNull() ? "" : qPrintable(n)) << "\" defined @" << info->startLine << ":" << info->startColumn << std::endl; + } + } + } +#endif // DO_TRACE_INSTR + Q_ASSERT(instr.args + instr.argc <= stackSize); + QV4::Value *args = stack + instr.args; + __qmljs_call_value(context, VALUEPTR(instr.result), /*thisObject*/0, VALUE(instr.dest), args, instr.argc); + MOTH_END_INSTR(CallValue) + + MOTH_BEGIN_INSTR(CallProperty) + TRACE(property name, "%s, args=%u, argc=%u, this=%s", qPrintable(instr.name->toQString()), instr.args, instr.argc, (VALUE(instr.base)).toString(context)->toQString().toUtf8().constData()); + Q_ASSERT(instr.args + instr.argc <= stackSize); + QV4::Value *args = stack + instr.args; + __qmljs_call_property(context, VALUEPTR(instr.result), VALUE(instr.base), instr.name, args, instr.argc); + MOTH_END_INSTR(CallProperty) + + MOTH_BEGIN_INSTR(CallElement) + Q_ASSERT(instr.args + instr.argc <= stackSize); + QV4::Value *args = stack + instr.args; + __qmljs_call_element(context, VALUEPTR(instr.result), VALUE(instr.base), VALUE(instr.index), args, instr.argc); + MOTH_END_INSTR(CallElement) + + MOTH_BEGIN_INSTR(CallActivationProperty) + Q_ASSERT(instr.args + instr.argc <= stackSize); + TRACE(args, "starting at %d, length %d", instr.args, instr.argc); + QV4::Value *args = stack + instr.args; + __qmljs_call_activation_property(context, VALUEPTR(instr.result), instr.name, args, instr.argc); + MOTH_END_INSTR(CallActivationProperty) + + MOTH_BEGIN_INSTR(CallBuiltinThrow) + __qmljs_throw(context, VALUE(instr.arg)); + MOTH_END_INSTR(CallBuiltinThrow) + + MOTH_BEGIN_INSTR(EnterTry) + VALUE(instr.exceptionVar) = QV4::Value::undefinedValue(); + try { + const uchar *tryCode = ((uchar *)&instr.tryOffset) + instr.tryOffset; + run(context, tryCode, stack, stackSize); + code = tryCode; + context->interpreterInstructionPointer = &code; + } catch (QV4::Exception &ex) { + ex.accept(context); + VALUE(instr.exceptionVar) = ex.value(); + try { + QV4::ExecutionContext *catchContext = __qmljs_builtin_push_catch_scope(instr.exceptionVarName, ex.value(), context); + const uchar *catchCode = ((uchar *)&instr.catchOffset) + instr.catchOffset; + run(catchContext, catchCode, stack, stackSize); + code = catchCode; + context->interpreterInstructionPointer = &code; + context = __qmljs_builtin_pop_scope(catchContext); + } catch (QV4::Exception &ex) { + ex.accept(context); + VALUE(instr.exceptionVar) = ex.value(); + const uchar *catchCode = ((uchar *)&instr.catchOffset) + instr.catchOffset; + run(context, catchCode, stack, stackSize); + code = catchCode; + context->interpreterInstructionPointer = &code; + } + } + MOTH_END_INSTR(EnterTry) + + MOTH_BEGIN_INSTR(CallBuiltinFinishTry) + return QV4::Value(); + MOTH_END_INSTR(CallBuiltinFinishTry) + + MOTH_BEGIN_INSTR(CallBuiltinPushScope) + context = __qmljs_builtin_push_with_scope(VALUE(instr.arg), context); + MOTH_END_INSTR(CallBuiltinPushScope) + + MOTH_BEGIN_INSTR(CallBuiltinPopScope) + context = __qmljs_builtin_pop_scope(context); + MOTH_END_INSTR(CallBuiltinPopScope) + + MOTH_BEGIN_INSTR(CallBuiltinForeachIteratorObject) + __qmljs_foreach_iterator_object(context, VALUEPTR(instr.result), VALUE(instr.arg)); + MOTH_END_INSTR(CallBuiltinForeachIteratorObject) + + MOTH_BEGIN_INSTR(CallBuiltinForeachNextPropertyName) + __qmljs_foreach_next_property_name(VALUEPTR(instr.result), VALUE(instr.arg)); + MOTH_END_INSTR(CallBuiltinForeachNextPropertyName) + + MOTH_BEGIN_INSTR(CallBuiltinDeleteMember) + __qmljs_delete_member(context, VALUEPTR(instr.result), VALUE(instr.base), instr.member); + MOTH_END_INSTR(CallBuiltinDeleteMember) + + MOTH_BEGIN_INSTR(CallBuiltinDeleteSubscript) + __qmljs_delete_subscript(context, VALUEPTR(instr.result), VALUE(instr.base), VALUE(instr.index)); + MOTH_END_INSTR(CallBuiltinDeleteSubscript) + + MOTH_BEGIN_INSTR(CallBuiltinDeleteName) + __qmljs_delete_name(context, VALUEPTR(instr.result), instr.name); + MOTH_END_INSTR(CallBuiltinDeleteName) + + MOTH_BEGIN_INSTR(CallBuiltinTypeofMember) + __qmljs_builtin_typeof_member(context, VALUEPTR(instr.result), VALUE(instr.base), instr.member); + MOTH_END_INSTR(CallBuiltinTypeofMember) + + MOTH_BEGIN_INSTR(CallBuiltinTypeofSubscript) + __qmljs_builtin_typeof_element(context, VALUEPTR(instr.result), VALUE(instr.base), VALUE(instr.index)); + MOTH_END_INSTR(CallBuiltinTypeofSubscript) + + MOTH_BEGIN_INSTR(CallBuiltinTypeofName) + __qmljs_builtin_typeof_name(context, VALUEPTR(instr.result), instr.name); + MOTH_END_INSTR(CallBuiltinTypeofName) + + MOTH_BEGIN_INSTR(CallBuiltinTypeofValue) + __qmljs_builtin_typeof(context, VALUEPTR(instr.result), VALUE(instr.value)); + MOTH_END_INSTR(CallBuiltinTypeofValue) + + MOTH_BEGIN_INSTR(CallBuiltinPostIncMember) + __qmljs_builtin_post_increment_member(context, VALUEPTR(instr.result), VALUE(instr.base), instr.member); + MOTH_END_INSTR(CallBuiltinTypeofMember) + + MOTH_BEGIN_INSTR(CallBuiltinPostIncSubscript) + __qmljs_builtin_post_increment_element(context, VALUEPTR(instr.result), VALUE(instr.base), VALUEPTR(instr.index)); + MOTH_END_INSTR(CallBuiltinTypeofSubscript) + + MOTH_BEGIN_INSTR(CallBuiltinPostIncName) + __qmljs_builtin_post_increment_name(context, VALUEPTR(instr.result), instr.name); + MOTH_END_INSTR(CallBuiltinTypeofName) + + MOTH_BEGIN_INSTR(CallBuiltinPostIncValue) + __qmljs_builtin_post_increment(VALUEPTR(instr.result), VALUEPTR(instr.value)); + MOTH_END_INSTR(CallBuiltinTypeofValue) + + MOTH_BEGIN_INSTR(CallBuiltinPostDecMember) + __qmljs_builtin_post_decrement_member(context, VALUEPTR(instr.result), VALUE(instr.base), instr.member); + MOTH_END_INSTR(CallBuiltinTypeofMember) + + MOTH_BEGIN_INSTR(CallBuiltinPostDecSubscript) + __qmljs_builtin_post_decrement_element(context, VALUEPTR(instr.result), VALUE(instr.base), VALUE(instr.index)); + MOTH_END_INSTR(CallBuiltinTypeofSubscript) + + MOTH_BEGIN_INSTR(CallBuiltinPostDecName) + __qmljs_builtin_post_decrement_name(context, VALUEPTR(instr.result), instr.name); + MOTH_END_INSTR(CallBuiltinTypeofName) + + MOTH_BEGIN_INSTR(CallBuiltinPostDecValue) + __qmljs_builtin_post_decrement(VALUEPTR(instr.result), VALUEPTR(instr.value)); + MOTH_END_INSTR(CallBuiltinTypeofValue) + + MOTH_BEGIN_INSTR(CallBuiltinDeclareVar) + __qmljs_builtin_declare_var(context, instr.isDeletable, instr.varName); + MOTH_END_INSTR(CallBuiltinDeclareVar) + + MOTH_BEGIN_INSTR(CallBuiltinDefineGetterSetter) + __qmljs_builtin_define_getter_setter(context, VALUE(instr.object), instr.name, VALUEPTR(instr.getter), VALUEPTR(instr.setter)); + MOTH_END_INSTR(CallBuiltinDefineGetterSetter) + + MOTH_BEGIN_INSTR(CallBuiltinDefineProperty) + __qmljs_builtin_define_property(context, VALUE(instr.object), instr.name, VALUEPTR(instr.value)); + MOTH_END_INSTR(CallBuiltinDefineProperty) + + MOTH_BEGIN_INSTR(CallBuiltinDefineArray) + Q_ASSERT(instr.args + instr.argc <= stackSize); + QV4::Value *args = stack + instr.args; + __qmljs_builtin_define_array(context, VALUEPTR(instr.result), args, instr.argc); + MOTH_END_INSTR(CallBuiltinDefineArray) + + MOTH_BEGIN_INSTR(CallBuiltinDefineObjectLiteral) + QV4::Value *args = stack + instr.args; + __qmljs_builtin_define_object_literal(context, VALUEPTR(instr.result), args, instr.internalClass); + MOTH_END_INSTR(CallBuiltinDefineObjectLiteral) + + MOTH_BEGIN_INSTR(CreateValue) + Q_ASSERT(instr.args + instr.argc <= stackSize); + QV4::Value *args = stack + instr.args; + __qmljs_construct_value(context, VALUEPTR(instr.result), VALUE(instr.func), args, instr.argc); + MOTH_END_INSTR(CreateValue) + + MOTH_BEGIN_INSTR(CreateProperty) + Q_ASSERT(instr.args + instr.argc <= stackSize); + QV4::Value *args = stack + instr.args; + __qmljs_construct_property(context, VALUEPTR(instr.result), VALUE(instr.base), instr.name, args, instr.argc); + MOTH_END_INSTR(CreateProperty) + + MOTH_BEGIN_INSTR(CreateActivationProperty) + TRACE(inline, "property name = %s, args = %d, argc = %d", instr.name->toQString().toUtf8().constData(), instr.args, instr.argc); + Q_ASSERT(instr.args + instr.argc <= stackSize); + QV4::Value *args = stack + instr.args; + __qmljs_construct_activation_property(context, VALUEPTR(instr.result), instr.name, args, instr.argc); + MOTH_END_INSTR(CreateActivationProperty) + + MOTH_BEGIN_INSTR(Jump) + code = ((uchar *)&instr.offset) + instr.offset; + MOTH_END_INSTR(Jump) + + MOTH_BEGIN_INSTR(CJump) + uint cond = __qmljs_to_boolean(VALUE(instr.condition)); + TRACE(condition, "%s", cond ? "TRUE" : "FALSE"); + if (cond) + code = ((uchar *)&instr.offset) + instr.offset; + MOTH_END_INSTR(CJump) + + MOTH_BEGIN_INSTR(Unop) + instr.alu(VALUEPTR(instr.result), VALUE(instr.source)); + MOTH_END_INSTR(Unop) + + MOTH_BEGIN_INSTR(Binop) + instr.alu(VALUEPTR(instr.result), VALUE(instr.lhs), VALUE(instr.rhs)); + MOTH_END_INSTR(Binop) + + MOTH_BEGIN_INSTR(BinopContext) + instr.alu(context, VALUEPTR(instr.result), VALUE(instr.lhs), VALUE(instr.rhs)); + MOTH_END_INSTR(BinopContext) + + MOTH_BEGIN_INSTR(AddNumberParams) + QV4::Value lhs = VALUE(instr.lhs); + QV4::Value rhs = VALUE(instr.rhs); + if (lhs.isInteger() && rhs.isInteger()) + VALUE(instr.result) = QV4::add_int32(lhs.integerValue(), rhs.integerValue()); + else + VALUEPTR(instr.result)->setDouble(lhs.asDouble() + rhs.asDouble()); + MOTH_END_INSTR(AddNumberParams) + + MOTH_BEGIN_INSTR(MulNumberParams) + QV4::Value lhs = VALUE(instr.lhs); + QV4::Value rhs = VALUE(instr.rhs); + if (lhs.isInteger() && rhs.isInteger()) + VALUE(instr.result) = QV4::mul_int32(lhs.integerValue(), rhs.integerValue()); + else + VALUEPTR(instr.result)->setDouble(lhs.asDouble() * rhs.asDouble()); + MOTH_END_INSTR(MulNumberParams) + + MOTH_BEGIN_INSTR(SubNumberParams) + QV4::Value lhs = VALUE(instr.lhs); + QV4::Value rhs = VALUE(instr.rhs); + if (lhs.isInteger() && rhs.isInteger()) + VALUE(instr.result) = QV4::sub_int32(lhs.integerValue(), rhs.integerValue()); + else + VALUEPTR(instr.result)->setDouble(lhs.asDouble() - rhs.asDouble()); + MOTH_END_INSTR(SubNumberParams) + + MOTH_BEGIN_INSTR(Ret) + QV4::Value &result = VALUE(instr.result); +// TRACE(Ret, "returning value %s", result.toString(context)->toQString().toUtf8().constData()); + return result; + MOTH_END_INSTR(Ret) + + MOTH_BEGIN_INSTR(LoadThis) + VALUE(instr.result) = context->thisObject; + MOTH_END_INSTR(LoadThis) + + MOTH_BEGIN_INSTR(InplaceElementOp) + instr.alu(context, + VALUE(instr.base), + VALUE(instr.index), + VALUE(instr.source)); + MOTH_END_INSTR(InplaceElementOp) + + MOTH_BEGIN_INSTR(InplaceMemberOp) + instr.alu(context, + VALUE(instr.base), + instr.member, + VALUE(instr.source)); + MOTH_END_INSTR(InplaceMemberOp) + + MOTH_BEGIN_INSTR(InplaceNameOp) + TRACE(name, "%s", instr.name->toQString().toUtf8().constData()); + instr.alu(context, instr.name, VALUE(instr.source)); + MOTH_END_INSTR(InplaceNameOp) + +#ifdef MOTH_THREADED_INTERPRETER + // nothing to do +#else + default: + qFatal("QQmlJS::Moth::VME: Internal error - unknown instruction %d", genericInstr->common.instructionType); + break; + } + } +#endif + +} + +#ifdef MOTH_THREADED_INTERPRETER +void **VME::instructionJumpTable() +{ + static void **jumpTable = 0; + if (!jumpTable) { + const uchar *code = 0; + VME().run(0, code, 0, 0, &jumpTable); + } + return jumpTable; +} +#endif + +QV4::Value VME::exec(QV4::ExecutionContext *ctxt, const uchar *code) +{ + VME vme; + return vme.run(ctxt, code); +} diff --git a/src/qml/compiler/qv4vme_moth_p.h b/src/qml/compiler/qv4vme_moth_p.h new file mode 100644 index 0000000000..59692500ba --- /dev/null +++ b/src/qml/compiler/qv4vme_moth_p.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4VME_MOTH_P_H +#define QV4VME_MOTH_P_H + +#include +#include "qv4instr_moth_p.h" + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { +namespace Moth { + +class VME +{ +public: + static QV4::Value exec(QV4::ExecutionContext *, const uchar *); + +#ifdef MOTH_THREADED_INTERPRETER + static void **instructionJumpTable(); +#endif + +private: + QV4::Value run(QV4::ExecutionContext *, const uchar *&code, + QV4::Value *stack = 0, unsigned stackSize = 0 +#ifdef MOTH_THREADED_INTERPRETER + , void ***storeJumpTable = 0 +#endif + ); +}; + +} // namespace Moth +} // namespace QQmlJS + +QT_END_NAMESPACE + +#endif // QV4VME_MOTH_P_H diff --git a/src/qml/jsruntime/jsruntime.pri b/src/qml/jsruntime/jsruntime.pri new file mode 100644 index 0000000000..a8f5f11a50 --- /dev/null +++ b/src/qml/jsruntime/jsruntime.pri @@ -0,0 +1,144 @@ +CONFIG += exceptions + +CONFIG += warn_off + +INCLUDEPATH += $$PWD +INCLUDEPATH += $$OUT_PWD + +SOURCES += \ + $$PWD/qv4engine.cpp \ + $$PWD/qv4context.cpp \ + $$PWD/qv4runtime.cpp \ + $$PWD/qv4value.cpp \ + $$PWD/qv4debugging.cpp \ + $$PWD/qv4lookup.cpp \ + $$PWD/qv4identifier.cpp \ + $$PWD/qv4identifiertable.cpp \ + $$PWD/qv4mm.cpp \ + $$PWD/qv4managed.cpp \ + $$PWD/qv4internalclass.cpp \ + $$PWD/qv4sparsearray.cpp \ + $$PWD/qv4arrayobject.cpp \ + $$PWD/qv4argumentsobject.cpp \ + $$PWD/qv4booleanobject.cpp \ + $$PWD/qv4dateobject.cpp \ + $$PWD/qv4errorobject.cpp \ + $$PWD/qv4function.cpp \ + $$PWD/qv4functionobject.cpp \ + $$PWD/qv4globalobject.cpp \ + $$PWD/qv4jsonobject.cpp \ + $$PWD/qv4mathobject.cpp \ + $$PWD/qv4numberobject.cpp \ + $$PWD/qv4object.cpp \ + $$PWD/qv4objectproto.cpp \ + $$PWD/qv4regexpobject.cpp \ + $$PWD/qv4stringobject.cpp \ + $$PWD/qv4variantobject.cpp \ + $$PWD/qv4string.cpp \ + $$PWD/qv4objectiterator.cpp \ + $$PWD/qv4regexp.cpp \ + $$PWD/qv4unwindhelper.cpp \ + $$PWD/qv4serialize.cpp \ + $$PWD/qv4script.cpp \ + $$PWD/qv4executableallocator.cpp \ + $$PWD/qv4sequenceobject.cpp \ + $$PWD/qv4include.cpp \ + $$PWD/qv4qobjectwrapper.cpp \ + $$PWD/qv4qmlextensions.cpp \ + $$PWD/qv4stacktrace.cpp \ + $$PWD/qv4exception.cpp + +HEADERS += \ + $$PWD/qv4global_p.h \ + $$PWD/qv4engine_p.h \ + $$PWD/qv4context_p.h \ + $$PWD/qv4runtime_p.h \ + $$PWD/qv4math_p.h \ + $$PWD/qv4value_p.h \ + $$PWD/qv4value_def_p.h \ + $$PWD/qv4debugging_p.h \ + $$PWD/qv4lookup_p.h \ + $$PWD/qv4identifier_p.h \ + $$PWD/qv4identifiertable_p.h \ + $$PWD/qv4mm_p.h \ + $$PWD/qv4managed_p.h \ + $$PWD/qv4internalclass_p.h \ + $$PWD/qv4sparsearray_p.h \ + $$PWD/qv4arrayobject_p.h \ + $$PWD/qv4argumentsobject_p.h \ + $$PWD/qv4booleanobject_p.h \ + $$PWD/qv4dateobject_p.h \ + $$PWD/qv4errorobject_p.h \ + $$PWD/qv4function_p.h \ + $$PWD/qv4functionobject_p.h \ + $$PWD/qv4globalobject_p.h \ + $$PWD/qv4jsonobject_p.h \ + $$PWD/qv4mathobject_p.h \ + $$PWD/qv4numberobject_p.h \ + $$PWD/qv4object_p.h \ + $$PWD/qv4objectproto_p.h \ + $$PWD/qv4regexpobject_p.h \ + $$PWD/qv4stringobject_p.h \ + $$PWD/qv4variantobject_p.h \ + $$PWD/qv4string_p.h \ + $$PWD/qv4property_p.h \ + $$PWD/qv4objectiterator_p.h \ + $$PWD/qv4regexp_p.h \ + $$PWD/qv4unwindhelper_p.h \ + $$PWD/qv4unwindhelper_p-dw2.h \ + $$PWD/qv4unwindhelper_p-arm.h \ + $$PWD/qv4serialize_p.h \ + $$PWD/qv4script_p.h \ + $$PWD/qv4util_p.h \ + $$PWD/qv4executableallocator_p.h \ + $$PWD/qv4sequenceobject_p.h \ + $$PWD/qv4include_p.h \ + $$PWD/qv4qobjectwrapper_p.h \ + $$PWD/qv4qmlextensions_p.h \ + $$PWD/qv4stacktrace_p.h \ + $$PWD/qv4exception_p.h + +# Use SSE2 floating point math on 32 bit instead of the default +# 387 to make test results pass on 32 and on 64 bit builds. +linux-g++*:isEqual(QT_ARCH,i386) { + QMAKE_CFLAGS += -march=pentium4 -msse2 -mfpmath=sse + QMAKE_CXXFLAGS += -march=pentium4 -msse2 -mfpmath=sse +} + +linux*|mac { + LIBS += -ldl +} + +# Only on Android/ARM at the moment, because only there we have issues +# replacing __gnu_Unwind_Find_exidx with our own implementation, +# and thus require static libgcc linkage. +android:equals(QT_ARCH, "arm"):*g++* { + static_libgcc = $$system($$QMAKE_CXX -print-file-name=libgcc.a) + LIBS += $$static_libgcc + SOURCES += $$PWD/qv4exception_gcc.cpp + DEFINES += V4_CXX_ABI_EXCEPTION +} + +debug-with-libunwind { + UW_INC=$$(LIBUNWIND_INCLUDES) + isEmpty(UW_INC): error("Please set LIBUNWIND_INCLUDES") + INCLUDEPATH += $$UW_INC + UW_LIBS=$$(LIBUNWIND_LIBS) + isEmpty(UW_LIBS): error("Please set LIBUNWIND_LIBS") + LIBS += -L$$UW_LIBS + equals(QT_ARCH, arm): LIBS += -lunwind-arm + LIBS += -lunwind-dwarf-common -lunwind-dwarf-local -lunwind-elf32 -lunwind + DEFINES += WTF_USE_LIBUNWIND_DEBUG=1 +} + +valgrind { + DEFINES += V4_USE_VALGRIND +} + +ios: DEFINES += ENABLE_ASSEMBLER_WX_EXCLUSIVE=1 + +win32 { + LIBS_PRIVATE += -lDbgHelp +} + +include(../../3rdparty/double-conversion/double-conversion.pri) diff --git a/src/qml/jsruntime/qv4alloca_p.h b/src/qml/jsruntime/qv4alloca_p.h new file mode 100644 index 0000000000..e4580da3d8 --- /dev/null +++ b/src/qml/jsruntime/qv4alloca_p.h @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4_ALLOCA_H +#define QV4_ALLOCA_H + +#include + +#if defined(Q_OS_WIN) +#include +#define alloca _alloca +#else +#include +#endif + +#endif diff --git a/src/qml/jsruntime/qv4argumentsobject.cpp b/src/qml/jsruntime/qv4argumentsobject.cpp new file mode 100644 index 0000000000..6247ef1504 --- /dev/null +++ b/src/qml/jsruntime/qv4argumentsobject.cpp @@ -0,0 +1,171 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include + +using namespace QV4; + +static Value throwTypeError(SimpleCallContext *ctx) +{ + ctx->throwTypeError(); + return Value::undefinedValue(); +} + +DEFINE_MANAGED_VTABLE(ArgumentsObject); + +ArgumentsObject::ArgumentsObject(CallContext *context, int formalParameterCount, int actualParameterCount) + : Object(context->engine), context(context) +{ + vtbl = &static_vtbl; + type = Type_ArgumentsObject; + + defineDefaultProperty(context->engine->id_length, Value::fromInt32(actualParameterCount)); + if (context->strictMode) { + for (uint i = 0; i < context->argumentCount; ++i) + Object::put(context, QString::number(i), context->arguments[i]); + FunctionObject *thrower = context->engine->newBuiltinFunction(context, 0, throwTypeError); + Property pd = Property::fromAccessor(thrower, thrower); + __defineOwnProperty__(context, QStringLiteral("callee"), pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); + __defineOwnProperty__(context, QStringLiteral("caller"), pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); + } else { + uint numAccessors = qMin(formalParameterCount, actualParameterCount); + context->engine->requireArgumentsAccessors(numAccessors); + for (uint i = 0; i < (uint)numAccessors; ++i) { + mappedArguments.append(context->argument(i)); + __defineOwnProperty__(context, i, context->engine->argumentsAccessors.at(i), Attr_Accessor); + } + for (uint i = numAccessors; i < qMin((uint)actualParameterCount, context->argumentCount); ++i) { + Property pd = Property::fromValue(context->argument(i)); + __defineOwnProperty__(context, i, pd, Attr_Data); + } + defineDefaultProperty(context, QStringLiteral("callee"), Value::fromObject(context->function)); + isNonStrictArgumentsObject = true; + } +} + +void ArgumentsObject::destroy(Managed *that) +{ + static_cast(that)->~ArgumentsObject(); +} + +bool ArgumentsObject::defineOwnProperty(ExecutionContext *ctx, uint index, const Property &desc, PropertyAttributes attrs) +{ + uint pidx = propertyIndexFromArrayIndex(index); + Property *pd = arrayData + pidx; + Property map; + PropertyAttributes mapAttrs; + bool isMapped = false; + if (pd && index < (uint)mappedArguments.size()) + isMapped = arrayAttributes && arrayAttributes[pidx].isAccessor() && pd->getter() == context->engine->argumentsAccessors.at(index).getter(); + + if (isMapped) { + map = *pd; + mapAttrs = arrayAttributes[pidx]; + arrayAttributes[pidx] = Attr_Data; + pd->value = mappedArguments.at(index); + } + + isNonStrictArgumentsObject = false; + bool strict = ctx->strictMode; + ctx->strictMode = false; + bool result = Object::__defineOwnProperty__(ctx, index, desc, attrs); + ctx->strictMode = strict; + isNonStrictArgumentsObject = true; + + if (isMapped && attrs.isData()) { + if (!attrs.isGeneric()) { + Value arg = desc.value; + map.setter()->call(Value::fromObject(this), &arg, 1); + } + if (attrs.isWritable()) { + *pd = map; + arrayAttributes[pidx] = mapAttrs; + } + } + + if (ctx->strictMode && !result) + ctx->throwTypeError(); + return result; +} + +DEFINE_MANAGED_VTABLE(ArgumentsGetterFunction); + +Value ArgumentsGetterFunction::call(Managed *getter, const Value &thisObject, Value *, int) +{ + ArgumentsGetterFunction *g = static_cast(getter); + Object *that = thisObject.asObject(); + if (!that) + getter->engine()->current->throwTypeError(); + ArgumentsObject *o = that->asArgumentsObject(); + if (!o) + getter->engine()->current->throwTypeError(); + + assert(g->index < o->context->argumentCount); + return o->context->argument(g->index); +} + +DEFINE_MANAGED_VTABLE(ArgumentsSetterFunction); + +Value ArgumentsSetterFunction::call(Managed *setter, const Value &thisObject, Value *args, int argc) +{ + ArgumentsSetterFunction *s = static_cast(setter); + Object *that = thisObject.asObject(); + if (!that) + setter->engine()->current->throwTypeError(); + ArgumentsObject *o = that->asArgumentsObject(); + if (!o) + setter->engine()->current->throwTypeError(); + + assert(s->index < o->context->argumentCount); + o->context->arguments[s->index] = argc ? args[0] : Value::undefinedValue(); + return Value::undefinedValue(); +} + +void ArgumentsObject::markObjects(Managed *that) +{ + ArgumentsObject *o = static_cast(that); + o->context->mark(); + for (int i = 0; i < o->mappedArguments.size(); ++i) { + Managed *m = o->mappedArguments.at(i).asManaged(); + if (m) + m->mark(); + } + Object::markObjects(that); +} diff --git a/src/qml/jsruntime/qv4argumentsobject_p.h b/src/qml/jsruntime/qv4argumentsobject_p.h new file mode 100644 index 0000000000..3d760b8937 --- /dev/null +++ b/src/qml/jsruntime/qv4argumentsobject_p.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4ARGUMENTSOBJECTS_H +#define QV4ARGUMENTSOBJECTS_H + +#include "qv4object_p.h" +#include "qv4functionobject_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct ArgumentsGetterFunction: FunctionObject +{ + uint index; + + ArgumentsGetterFunction(ExecutionContext *scope, uint index) + : FunctionObject(scope), index(index) { vtbl = &static_vtbl; } + + static Value call(Managed *that, const Value &, Value *, int); + +protected: + static const ManagedVTable static_vtbl; +}; + +struct ArgumentsSetterFunction: FunctionObject +{ + uint index; + + ArgumentsSetterFunction(ExecutionContext *scope, uint index) + : FunctionObject(scope), index(index) { vtbl = &static_vtbl; } + + static Value call(Managed *that, const Value &, Value *, int); + +protected: + static const ManagedVTable static_vtbl; +}; + + +struct ArgumentsObject: Object { + CallContext *context; + QVector mappedArguments; + ArgumentsObject(CallContext *context, int formalParameterCount, int actualParameterCount); + ~ArgumentsObject() {} + + bool defineOwnProperty(ExecutionContext *ctx, uint index, const Property &desc, PropertyAttributes attrs); + + static void markObjects(Managed *that); +protected: + static const ManagedVTable static_vtbl; + static void destroy(Managed *); +}; + +} + +QT_END_NAMESPACE + +#endif + diff --git a/src/qml/jsruntime/qv4arrayobject.cpp b/src/qml/jsruntime/qv4arrayobject.cpp new file mode 100644 index 0000000000..b1f25177dd --- /dev/null +++ b/src/qml/jsruntime/qv4arrayobject.cpp @@ -0,0 +1,865 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4arrayobject_p.h" +#include "qv4sparsearray_p.h" + +using namespace QV4; + +DEFINE_MANAGED_VTABLE(ArrayCtor); + +ArrayCtor::ArrayCtor(ExecutionContext *scope) + : FunctionObject(scope, scope->engine->newIdentifier(QStringLiteral("Array"))) +{ + vtbl = &static_vtbl; +} + +Value ArrayCtor::construct(Managed *m, Value *argv, int argc) +{ + ExecutionEngine *v4 = m->engine(); + ArrayObject *a = v4->newArrayObject(); + uint len; + if (argc == 1 && argv[0].isNumber()) { + bool ok; + len = argv[0].asArrayLength(&ok); + + if (!ok) + v4->current->throwRangeError(argv[0]); + + if (len < 0x1000) + a->arrayReserve(len); + } else { + len = argc; + a->arrayReserve(len); + for (unsigned int i = 0; i < len; ++i) + a->arrayData[i].value = argv[i]; + a->arrayDataLen = len; + } + a->setArrayLengthUnchecked(len); + + return Value::fromObject(a); +} + +Value ArrayCtor::call(Managed *that, const Value &, Value *argv, int argc) +{ + return construct(that, argv, argc); +} + +ArrayPrototype::ArrayPrototype(ExecutionContext *context) + : ArrayObject(context->engine) +{ +} + +void ArrayPrototype::init(ExecutionContext *ctx, const Value &ctor) +{ + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("isArray"), method_isArray, 1); + defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); + defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); + defineDefaultProperty(ctx, QStringLiteral("toLocaleString"), method_toLocaleString, 0); + defineDefaultProperty(ctx, QStringLiteral("concat"), method_concat, 1); + defineDefaultProperty(ctx, QStringLiteral("join"), method_join, 1); + defineDefaultProperty(ctx, QStringLiteral("pop"), method_pop, 0); + defineDefaultProperty(ctx, QStringLiteral("push"), method_push, 1); + defineDefaultProperty(ctx, QStringLiteral("reverse"), method_reverse, 0); + defineDefaultProperty(ctx, QStringLiteral("shift"), method_shift, 0); + defineDefaultProperty(ctx, QStringLiteral("slice"), method_slice, 2); + defineDefaultProperty(ctx, QStringLiteral("sort"), method_sort, 1); + defineDefaultProperty(ctx, QStringLiteral("splice"), method_splice, 2); + defineDefaultProperty(ctx, QStringLiteral("unshift"), method_unshift, 1); + defineDefaultProperty(ctx, QStringLiteral("indexOf"), method_indexOf, 1); + defineDefaultProperty(ctx, QStringLiteral("lastIndexOf"), method_lastIndexOf, 1); + defineDefaultProperty(ctx, QStringLiteral("every"), method_every, 1); + defineDefaultProperty(ctx, QStringLiteral("some"), method_some, 1); + defineDefaultProperty(ctx, QStringLiteral("forEach"), method_forEach, 1); + defineDefaultProperty(ctx, QStringLiteral("map"), method_map, 1); + defineDefaultProperty(ctx, QStringLiteral("filter"), method_filter, 1); + defineDefaultProperty(ctx, QStringLiteral("reduce"), method_reduce, 1); + defineDefaultProperty(ctx, QStringLiteral("reduceRight"), method_reduceRight, 1); +} + +uint ArrayPrototype::getLength(ExecutionContext *ctx, Object *o) +{ + if (o->isArrayObject()) + return o->arrayLength(); + return o->get(ctx->engine->id_length).toUInt32(); +} + +Value ArrayPrototype::method_isArray(SimpleCallContext *ctx) +{ + Value arg = ctx->argument(0); + bool isArray = arg.asArrayObject(); + return Value::fromBoolean(isArray); +} + +Value ArrayPrototype::method_toString(SimpleCallContext *ctx) +{ + return method_join(ctx); +} + +Value ArrayPrototype::method_toLocaleString(SimpleCallContext *ctx) +{ + return method_toString(ctx); +} + +Value ArrayPrototype::method_concat(SimpleCallContext *ctx) +{ + ArrayObject *result = ctx->engine->newArrayObject(); + + if (ArrayObject *instance = ctx->thisObject.asArrayObject()) { + result->copyArrayData(instance); + } else { + QString v = ctx->thisObject.toString(ctx)->toQString(); + result->arraySet(0, Value::fromString(ctx, v)); + } + + for (uint i = 0; i < ctx->argumentCount; ++i) { + Value arg = ctx->argument(i); + + if (ArrayObject *elt = arg.asArrayObject()) + result->arrayConcat(elt); + + else + result->arraySet(getLength(ctx, result), arg); + } + + return Value::fromObject(result); +} + +Value ArrayPrototype::method_join(SimpleCallContext *ctx) +{ + Value arg = ctx->argument(0); + + QString r4; + if (arg.isUndefined()) + r4 = QStringLiteral(","); + else + r4 = arg.toString(ctx)->toQString(); + + Value self = ctx->thisObject; + const Value length = self.property(ctx, ctx->engine->id_length); + const quint32 r2 = Value::toUInt32(length.isUndefined() ? 0 : length.toNumber()); + + static QSet visitedArrayElements; + + if (! r2 || visitedArrayElements.contains(self.objectValue())) + return Value::fromString(ctx, QString()); + + // avoid infinite recursion + visitedArrayElements.insert(self.objectValue()); + + QString R; + + // ### FIXME + if (ArrayObject *a = self.asArrayObject()) { + for (uint i = 0; i < a->arrayLength(); ++i) { + if (i) + R += r4; + + Value e = a->getIndexed(i); + if (! (e.isUndefined() || e.isNull())) + R += e.toString(ctx)->toQString(); + } + } else { + // + // crazy! + // + Value r6 = self.property(ctx, ctx->engine->newString(QStringLiteral("0"))); + if (!(r6.isUndefined() || r6.isNull())) + R = r6.toString(ctx)->toQString(); + + for (quint32 k = 1; k < r2; ++k) { + R += r4; + + String *name = Value::fromDouble(k).toString(ctx); + Value r12 = self.property(ctx, name); + + if (! (r12.isUndefined() || r12.isNull())) + R += r12.toString(ctx)->toQString(); + } + } + + visitedArrayElements.remove(self.objectValue()); + return Value::fromString(ctx, R); +} + +Value ArrayPrototype::method_pop(SimpleCallContext *ctx) +{ + Object *instance = ctx->thisObject.toObject(ctx); + uint len = getLength(ctx, instance); + + if (!len) { + if (!instance->isArrayObject()) + instance->put(ctx->engine->id_length, Value::fromInt32(0)); + return Value::undefinedValue(); + } + + Value result = instance->getIndexed(len - 1); + + instance->deleteIndexedProperty(len - 1); + if (instance->isArrayObject()) + instance->setArrayLengthUnchecked(len - 1); + else + instance->put(ctx->engine->id_length, Value::fromDouble(len - 1)); + return result; +} + +Value ArrayPrototype::method_push(SimpleCallContext *ctx) +{ + Object *instance = ctx->thisObject.toObject(ctx); + uint len = getLength(ctx, instance); + + if (len + ctx->argumentCount < len) { + // ughh... + double l = len; + for (double i = 0; i < ctx->argumentCount; ++i) { + Value idx = Value::fromDouble(l + i); + instance->put(idx.toString(ctx), ctx->argument(i)); + } + double newLen = l + ctx->argumentCount; + if (!instance->isArrayObject()) + instance->put(ctx->engine->id_length, Value::fromDouble(newLen)); + else + ctx->throwRangeError(Value::fromString(ctx, QStringLiteral("Array.prototype.push: Overflow"))); + return Value::fromDouble(newLen); + } + + bool protoHasArray = false; + Object *p = instance; + while ((p = p->prototype)) + if (p->arrayDataLen) + protoHasArray = true; + + if (!protoHasArray && instance->arrayDataLen <= len) { + for (uint i = 0; i < ctx->argumentCount; ++i) { + Value v = ctx->argument(i); + + if (!instance->sparseArray) { + if (len >= instance->arrayAlloc) + instance->arrayReserve(len + 1); + instance->arrayData[len].value = v; + if (instance->arrayAttributes) + instance->arrayAttributes[len] = Attr_Data; + instance->arrayDataLen = len + 1; + } else { + uint i = instance->allocArrayValue(v); + instance->sparseArray->push_back(i, len); + } + ++len; + } + } else { + for (uint i = 0; i < ctx->argumentCount; ++i) + instance->putIndexed(len + i, ctx->argument(i)); + len += ctx->argumentCount; + } + if (instance->isArrayObject()) + instance->setArrayLengthUnchecked(len); + else + instance->put(ctx->engine->id_length, Value::fromDouble(len)); + + if (len < INT_MAX) + return Value::fromInt32(len); + return Value::fromDouble((double)len); + +} + +Value ArrayPrototype::method_reverse(SimpleCallContext *ctx) +{ + Object *instance = ctx->thisObject.toObject(ctx); + uint length = getLength(ctx, instance); + + int lo = 0, hi = length - 1; + + for (; lo < hi; ++lo, --hi) { + bool loExists, hiExists; + Value lval = instance->getIndexed(lo, &loExists); + Value hval = instance->getIndexed(hi, &hiExists); + if (hiExists) + instance->putIndexed(lo, hval); + else + instance->deleteIndexedProperty(lo); + if (loExists) + instance->putIndexed(hi, lval); + else + instance->deleteIndexedProperty(hi); + } + return Value::fromObject(instance); +} + +Value ArrayPrototype::method_shift(SimpleCallContext *ctx) +{ + Object *instance = ctx->thisObject.toObject(ctx); + uint len = getLength(ctx, instance); + + if (!len) { + if (!instance->isArrayObject()) + instance->put(ctx->engine->id_length, Value::fromInt32(0)); + return Value::undefinedValue(); + } + + Property *front = 0; + uint pidx = instance->propertyIndexFromArrayIndex(0); + if (pidx < UINT_MAX && (!instance->arrayAttributes || !instance->arrayAttributes[0].isGeneric())) + front = instance->arrayData + pidx; + + Value result = front ? instance->getValue(front, instance->arrayAttributes ? instance->arrayAttributes[pidx] : Attr_Data) : Value::undefinedValue(); + + bool protoHasArray = false; + Object *p = instance; + while ((p = p->prototype)) + if (p->arrayDataLen) + protoHasArray = true; + + if (!protoHasArray && instance->arrayDataLen <= len) { + if (!instance->sparseArray) { + if (instance->arrayDataLen) { + ++instance->arrayOffset; + ++instance->arrayData; + --instance->arrayDataLen; + --instance->arrayAlloc; + if (instance->arrayAttributes) + ++instance->arrayAttributes; + } + } else { + uint idx = instance->sparseArray->pop_front(); + instance->freeArrayValue(idx); + } + } else { + // do it the slow way + for (uint k = 1; k < len; ++k) { + bool exists; + Value v = instance->getIndexed(k, &exists); + if (exists) + instance->putIndexed(k - 1, v); + else + instance->deleteIndexedProperty(k - 1); + } + instance->deleteIndexedProperty(len - 1); + } + + if (instance->isArrayObject()) + instance->setArrayLengthUnchecked(len - 1); + else + instance->put(ctx->engine->id_length, Value::fromDouble(len - 1)); + return result; +} + +Value ArrayPrototype::method_slice(SimpleCallContext *ctx) +{ + Object *o = ctx->thisObject.toObject(ctx); + + ArrayObject *result = ctx->engine->newArrayObject(); + uint len = o->get(ctx->engine->id_length).toUInt32(); + double s = ctx->argument(0).toInteger(); + uint start; + if (s < 0) + start = (uint)qMax(len + s, 0.); + else if (s > len) + start = len; + else + start = (uint) s; + uint end = len; + if (!ctx->argument(1).isUndefined()) { + double e = ctx->argument(1).toInteger(); + if (e < 0) + end = (uint)qMax(len + e, 0.); + else if (e > len) + end = len; + else + end = (uint) e; + } + + uint n = 0; + for (uint i = start; i < end; ++i) { + bool exists; + Value v = o->getIndexed(i, &exists); + if (exists) { + result->arraySet(n, v); + } + ++n; + } + return Value::fromObject(result); +} + +Value ArrayPrototype::method_sort(SimpleCallContext *ctx) +{ + Object *instance = ctx->thisObject.toObject(ctx); + + uint len = getLength(ctx, instance); + + Value comparefn = ctx->argument(0); + instance->arraySort(ctx, instance, comparefn, len); + return ctx->thisObject; +} + +Value ArrayPrototype::method_splice(SimpleCallContext *ctx) +{ + Object *instance = ctx->thisObject.toObject(ctx); + uint len = getLength(ctx, instance); + + ArrayObject *newArray = ctx->engine->newArrayObject(); + + double rs = ctx->argument(0).toInteger(); + uint start; + if (rs < 0) + start = (uint) qMax(0., len + rs); + else + start = (uint) qMin(rs, (double)len); + + uint deleteCount = (uint)qMin(qMax(ctx->argument(1).toInteger(), 0.), (double)(len - start)); + + newArray->arrayReserve(deleteCount); + Property *pd = newArray->arrayData; + for (uint i = 0; i < deleteCount; ++i) { + pd->value = instance->getIndexed(start + i); + ++pd; + } + newArray->arrayDataLen = deleteCount; + newArray->setArrayLengthUnchecked(deleteCount); + + uint itemCount = ctx->argumentCount < 2 ? 0 : ctx->argumentCount - 2; + + if (itemCount < deleteCount) { + for (uint k = start; k < len - deleteCount; ++k) { + bool exists; + Value v = instance->getIndexed(k + deleteCount, &exists); + if (exists) + instance->putIndexed(k + itemCount, v); + else + instance->deleteIndexedProperty(k + itemCount); + } + for (uint k = len; k > len - deleteCount + itemCount; --k) + instance->deleteIndexedProperty(k - 1); + } else if (itemCount > deleteCount) { + uint k = len - deleteCount; + while (k > start) { + bool exists; + Value v = instance->getIndexed(k + deleteCount - 1, &exists); + if (exists) + instance->putIndexed(k + itemCount - 1, v); + else + instance->deleteIndexedProperty(k + itemCount - 1); + --k; + } + } + + for (uint i = 0; i < itemCount; ++i) + instance->putIndexed(start + i, ctx->argument(i + 2)); + + ctx->strictMode = true; + instance->put(ctx->engine->id_length, Value::fromDouble(len - deleteCount + itemCount)); + + return Value::fromObject(newArray); +} + +Value ArrayPrototype::method_unshift(SimpleCallContext *ctx) +{ + Object *instance = ctx->thisObject.toObject(ctx); + uint len = getLength(ctx, instance); + + bool protoHasArray = false; + Object *p = instance; + while ((p = p->prototype)) + if (p->arrayDataLen) + protoHasArray = true; + + if (!protoHasArray && instance->arrayDataLen <= len) { + for (int i = ctx->argumentCount - 1; i >= 0; --i) { + Value v = ctx->argument(i); + + if (!instance->sparseArray) { + if (!instance->arrayOffset) + instance->getArrayHeadRoom(); + + --instance->arrayOffset; + --instance->arrayData; + ++instance->arrayDataLen; + if (instance->arrayAttributes) { + --instance->arrayAttributes; + *instance->arrayAttributes = Attr_Data; + } + instance->arrayData->value = v; + } else { + uint idx = instance->allocArrayValue(v); + instance->sparseArray->push_front(idx); + } + } + } else { + for (uint k = len; k > 0; --k) { + bool exists; + Value v = instance->getIndexed(k - 1, &exists); + if (exists) + instance->putIndexed(k + ctx->argumentCount - 1, v); + else + instance->deleteIndexedProperty(k + ctx->argumentCount - 1); + } + for (uint i = 0; i < ctx->argumentCount; ++i) + instance->putIndexed(i, ctx->argument(i)); + } + + uint newLen = len + ctx->argumentCount; + if (instance->isArrayObject()) + instance->setArrayLengthUnchecked(newLen); + else + instance->put(ctx->engine->id_length, Value::fromDouble(newLen)); + + if (newLen < INT_MAX) + return Value::fromInt32(newLen); + return Value::fromDouble((double)newLen); +} + +Value ArrayPrototype::method_indexOf(SimpleCallContext *ctx) +{ + Object *instance = ctx->thisObject.toObject(ctx); + uint len = getLength(ctx, instance); + if (!len) + return Value::fromInt32(-1); + + Value searchValue; + uint fromIndex = 0; + + if (ctx->argumentCount >= 1) + searchValue = ctx->argument(0); + else + searchValue = Value::undefinedValue(); + + if (ctx->argumentCount >= 2) { + double f = ctx->argument(1).toInteger(); + if (f >= len) + return Value::fromInt32(-1); + if (f < 0) + f = qMax(len + f, 0.); + fromIndex = (uint) f; + } + + if (instance->isStringObject()) { + for (uint k = fromIndex; k < len; ++k) { + bool exists; + Value v = instance->getIndexed(k, &exists); + if (exists && __qmljs_strict_equal(v, searchValue)) + return Value::fromDouble(k); + } + return Value::fromInt32(-1); + } + + return instance->arrayIndexOf(searchValue, fromIndex, len, ctx, instance); +} + +Value ArrayPrototype::method_lastIndexOf(SimpleCallContext *ctx) +{ + Object *instance = ctx->thisObject.toObject(ctx); + uint len = getLength(ctx, instance); + if (!len) + return Value::fromInt32(-1); + + Value searchValue; + uint fromIndex = len; + + if (ctx->argumentCount >= 1) + searchValue = ctx->argument(0); + else + searchValue = Value::undefinedValue(); + + if (ctx->argumentCount >= 2) { + double f = ctx->argument(1).toInteger(); + if (f > 0) + f = qMin(f, (double)(len - 1)); + else if (f < 0) { + f = len + f; + if (f < 0) + return Value::fromInt32(-1); + } + fromIndex = (uint) f + 1; + } + + for (uint k = fromIndex; k > 0;) { + --k; + bool exists; + Value v = instance->getIndexed(k, &exists); + if (exists && __qmljs_strict_equal(v, searchValue)) + return Value::fromDouble(k); + } + return Value::fromInt32(-1); +} + +Value ArrayPrototype::method_every(SimpleCallContext *ctx) +{ + Object *instance = ctx->thisObject.toObject(ctx); + + uint len = getLength(ctx, instance); + + FunctionObject *callback = ctx->argument(0).asFunctionObject(); + if (!callback) + ctx->throwTypeError(); + + Value thisArg = ctx->argument(1); + + bool ok = true; + for (uint k = 0; ok && k < len; ++k) { + bool exists; + Value v = instance->getIndexed(k, &exists); + if (!exists) + continue; + + Value args[3]; + args[0] = v; + args[1] = Value::fromDouble(k); + args[2] = ctx->thisObject; + Value r = callback->call(thisArg, args, 3); + ok = r.toBoolean(); + } + return Value::fromBoolean(ok); +} + +Value ArrayPrototype::method_some(SimpleCallContext *ctx) +{ + Object *instance = ctx->thisObject.toObject(ctx); + + uint len = getLength(ctx, instance); + + FunctionObject *callback = ctx->argument(0).asFunctionObject(); + if (!callback) + ctx->throwTypeError(); + + Value thisArg = ctx->argument(1); + + for (uint k = 0; k < len; ++k) { + bool exists; + Value v = instance->getIndexed(k, &exists); + if (!exists) + continue; + + Value args[3]; + args[0] = v; + args[1] = Value::fromDouble(k); + args[2] = ctx->thisObject; + Value r = callback->call(thisArg, args, 3); + if (r.toBoolean()) + return Value::fromBoolean(true); + } + return Value::fromBoolean(false); +} + +Value ArrayPrototype::method_forEach(SimpleCallContext *ctx) +{ + Object *instance = ctx->thisObject.toObject(ctx); + + uint len = getLength(ctx, instance); + + FunctionObject *callback = ctx->argument(0).asFunctionObject(); + if (!callback) + ctx->throwTypeError(); + + Value thisArg = ctx->argument(1); + + for (uint k = 0; k < len; ++k) { + bool exists; + Value v = instance->getIndexed(k, &exists); + if (!exists) + continue; + + Value args[3]; + args[0] = v; + args[1] = Value::fromDouble(k); + args[2] = ctx->thisObject; + callback->call(thisArg, args, 3); + } + return Value::undefinedValue(); +} + +Value ArrayPrototype::method_map(SimpleCallContext *ctx) +{ + Object *instance = ctx->thisObject.toObject(ctx); + + uint len = getLength(ctx, instance); + + FunctionObject *callback = ctx->argument(0).asFunctionObject(); + if (!callback) + ctx->throwTypeError(); + + Value thisArg = ctx->argument(1); + + ArrayObject *a = ctx->engine->newArrayObject(); + a->arrayReserve(len); + a->setArrayLengthUnchecked(len); + + for (uint k = 0; k < len; ++k) { + bool exists; + Value v = instance->getIndexed(k, &exists); + if (!exists) + continue; + + Value args[3]; + args[0] = v; + args[1] = Value::fromDouble(k); + args[2] = ctx->thisObject; + Value mapped = callback->call(thisArg, args, 3); + a->arraySet(k, mapped); + } + return Value::fromObject(a); +} + +Value ArrayPrototype::method_filter(SimpleCallContext *ctx) +{ + Object *instance = ctx->thisObject.toObject(ctx); + + uint len = getLength(ctx, instance); + + FunctionObject *callback = ctx->argument(0).asFunctionObject(); + if (!callback) + ctx->throwTypeError(); + + Value thisArg = ctx->argument(1); + + ArrayObject *a = ctx->engine->newArrayObject(); + a->arrayReserve(len); + + uint to = 0; + for (uint k = 0; k < len; ++k) { + bool exists; + Value v = instance->getIndexed(k, &exists); + if (!exists) + continue; + + Value args[3]; + args[0] = v; + args[1] = Value::fromDouble(k); + args[2] = ctx->thisObject; + Value selected = callback->call(thisArg, args, 3); + if (selected.toBoolean()) { + a->arraySet(to, v); + ++to; + } + } + return Value::fromObject(a); +} + +Value ArrayPrototype::method_reduce(SimpleCallContext *ctx) +{ + Object *instance = ctx->thisObject.toObject(ctx); + + uint len = getLength(ctx, instance); + + FunctionObject *callback = ctx->argument(0).asFunctionObject(); + if (!callback) + ctx->throwTypeError(); + + uint k = 0; + Value acc; + if (ctx->argumentCount > 1) { + acc = ctx->argument(1); + } else { + bool kPresent = false; + while (k < len && !kPresent) { + Value v = instance->getIndexed(k, &kPresent); + if (kPresent) + acc = v; + ++k; + } + if (!kPresent) + ctx->throwTypeError(); + } + + while (k < len) { + bool kPresent; + Value v = instance->getIndexed(k, &kPresent); + if (kPresent) { + Value args[4]; + args[0] = acc; + args[1] = v; + args[2] = Value::fromDouble(k); + args[3] = ctx->thisObject; + acc = callback->call(Value::undefinedValue(), args, 4); + } + ++k; + } + return acc; +} + +Value ArrayPrototype::method_reduceRight(SimpleCallContext *ctx) +{ + Object *instance = ctx->thisObject.toObject(ctx); + + uint len = getLength(ctx, instance); + + FunctionObject *callback = ctx->argument(0).asFunctionObject(); + if (!callback) + ctx->throwTypeError(); + + if (len == 0) { + if (ctx->argumentCount == 1) + ctx->throwTypeError(); + return ctx->argument(1); + } + + uint k = len; + Value acc; + if (ctx->argumentCount > 1) { + acc = ctx->argument(1); + } else { + bool kPresent = false; + while (k > 0 && !kPresent) { + Value v = instance->getIndexed(k - 1, &kPresent); + if (kPresent) + acc = v; + --k; + } + if (!kPresent) + ctx->throwTypeError(); + } + + while (k > 0) { + bool kPresent; + Value v = instance->getIndexed(k - 1, &kPresent); + if (kPresent) { + Value args[4]; + args[0] = acc; + args[1] = v; + args[2] = Value::fromDouble(k - 1); + args[3] = ctx->thisObject; + acc = callback->call(Value::undefinedValue(), args, 4); + } + --k; + } + return acc; +} + diff --git a/src/qml/jsruntime/qv4arrayobject_p.h b/src/qml/jsruntime/qv4arrayobject_p.h new file mode 100644 index 0000000000..13c3882f4f --- /dev/null +++ b/src/qml/jsruntime/qv4arrayobject_p.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4ARRAYOBJECT_H +#define QV4ARRAYOBJECT_H + +#include "qv4object_p.h" +#include "qv4functionobject_p.h" +#include + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct ArrayCtor: FunctionObject +{ + ArrayCtor(ExecutionContext *scope); + + static Value construct(Managed *m, Value *args, int argc); + static Value call(Managed *that, const Value &, Value *, int); + +protected: + static const ManagedVTable static_vtbl; +}; + +struct ArrayPrototype: ArrayObject +{ + ArrayPrototype(ExecutionContext *context); + + void init(ExecutionContext *ctx, const Value &ctor); + + static uint getLength(ExecutionContext *ctx, Object *o); + + static Value method_isArray(SimpleCallContext *ctx); + static Value method_toString(SimpleCallContext *ctx); + static Value method_toLocaleString(SimpleCallContext *ctx); + static Value method_concat(SimpleCallContext *ctx); + static Value method_join(SimpleCallContext *ctx); + static Value method_pop(SimpleCallContext *ctx); + static Value method_push(SimpleCallContext *ctx); + static Value method_reverse(SimpleCallContext *ctx); + static Value method_shift(SimpleCallContext *ctx); + static Value method_slice(SimpleCallContext *ctx); + static Value method_sort(SimpleCallContext *ctx); + static Value method_splice(SimpleCallContext *ctx); + static Value method_unshift(SimpleCallContext *ctx); + static Value method_indexOf(SimpleCallContext *ctx); + static Value method_lastIndexOf(SimpleCallContext *ctx); + static Value method_every(SimpleCallContext *ctx); + static Value method_some(SimpleCallContext *ctx); + static Value method_forEach(SimpleCallContext *ctx); + static Value method_map(SimpleCallContext *ctx); + static Value method_filter(SimpleCallContext *ctx); + static Value method_reduce(SimpleCallContext *ctx); + static Value method_reduceRight(SimpleCallContext *ctx); +}; + + +} // namespace QV4 + +QT_END_NAMESPACE + +#endif // QV4ECMAOBJECTS_P_H diff --git a/src/qml/jsruntime/qv4booleanobject.cpp b/src/qml/jsruntime/qv4booleanobject.cpp new file mode 100644 index 0000000000..24678d23dc --- /dev/null +++ b/src/qml/jsruntime/qv4booleanobject.cpp @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4booleanobject_p.h" + +using namespace QV4; + +DEFINE_MANAGED_VTABLE(BooleanCtor); + +BooleanCtor::BooleanCtor(ExecutionContext *scope) + : FunctionObject(scope, scope->engine->newIdentifier("Boolean")) +{ + vtbl = &static_vtbl; +} + +Value BooleanCtor::construct(Managed *m, Value *args, int argc) +{ + bool n = argc ? args[0].toBoolean() : false; + return Value::fromObject(m->engine()->newBooleanObject(Value::fromBoolean(n))); +} + +Value BooleanCtor::call(Managed *, const Value &, Value *argv, int argc) +{ + bool value = argc ? argv[0].toBoolean() : 0; + return Value::fromBoolean(value); +} + +void BooleanPrototype::init(ExecutionContext *ctx, const Value &ctor) +{ + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); + defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); + defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString); + defineDefaultProperty(ctx, QStringLiteral("valueOf"), method_valueOf); +} + +Value BooleanPrototype::method_toString(SimpleCallContext *ctx) +{ + bool result; + if (ctx->thisObject.isBoolean()) { + result = ctx->thisObject.booleanValue(); + } else { + BooleanObject *thisObject = ctx->thisObject.asBooleanObject(); + if (!thisObject) + ctx->throwTypeError(); + result = thisObject->value.booleanValue(); + } + + return Value::fromString(ctx, QLatin1String(result ? "true" : "false")); +} + +Value BooleanPrototype::method_valueOf(SimpleCallContext *ctx) +{ + BooleanObject *thisObject = ctx->thisObject.asBooleanObject(); + if (!thisObject) + ctx->throwTypeError(); + + return thisObject->value; +} diff --git a/src/qml/jsruntime/qv4booleanobject_p.h b/src/qml/jsruntime/qv4booleanobject_p.h new file mode 100644 index 0000000000..3e5e7663f2 --- /dev/null +++ b/src/qml/jsruntime/qv4booleanobject_p.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4BOOLEANOBJECT_H +#define QBOOLEANOBJECT_H + +#include "qv4object_p.h" +#include "qv4functionobject_p.h" +#include + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct BooleanCtor: FunctionObject +{ + BooleanCtor(ExecutionContext *scope); + + static Value construct(Managed *, Value *args, int argc); + static Value call(Managed *that, const Value &, Value *, int); + +protected: + static const ManagedVTable static_vtbl; +}; + +struct BooleanPrototype: BooleanObject +{ + BooleanPrototype(ExecutionEngine *engine): BooleanObject(engine, Value::fromBoolean(false)) {} + void init(ExecutionContext *ctx, const Value &ctor); + + static Value method_toString(SimpleCallContext *ctx); + static Value method_valueOf(SimpleCallContext *ctx); +}; + + +} + +QT_END_NAMESPACE + +#endif // QV4ECMAOBJECTS_P_H diff --git a/src/qml/jsruntime/qv4context.cpp b/src/qml/jsruntime/qv4context.cpp new file mode 100644 index 0000000000..ce947e51e8 --- /dev/null +++ b/src/qml/jsruntime/qv4context.cpp @@ -0,0 +1,615 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include "qv4debugging_p.h" +#include +#include +#include +#include "qv4mm_p.h" +#include +#include "qv4function_p.h" + +using namespace QV4; + +DiagnosticMessage::DiagnosticMessage() + : offset(0) + , length(0) + , startLine(0) + , startColumn(0) + , type(0) + , next(0) +{} + +DiagnosticMessage::~DiagnosticMessage() +{ + delete next; +} + +String *DiagnosticMessage::buildFullMessage(ExecutionContext *ctx) const +{ + QString msg; + if (!fileName.isEmpty()) + msg = fileName + QLatin1Char(':'); + msg += QString::number(startLine) + QLatin1Char(':') + QString::number(startColumn) + QLatin1String(": "); + if (type == QV4::DiagnosticMessage::Error) + msg += QLatin1String("error"); + else + msg += QLatin1String("warning"); + msg += ": " + message; + + return ctx->engine->newString(msg); +} + +void ExecutionContext::createMutableBinding(String *name, bool deletable) +{ + + // find the right context to create the binding on + Object *activation = engine->globalObject; + ExecutionContext *ctx = this; + while (ctx) { + if (ctx->type >= Type_CallContext) { + CallContext *c = static_cast(ctx); + if (!c->activation) + c->activation = engine->newObject(); + activation = c->activation; + break; + } + ctx = ctx->outer; + } + + if (activation->__hasProperty__(name)) + return; + Property desc = Property::fromValue(Value::undefinedValue()); + PropertyAttributes attrs(Attr_Data); + attrs.setConfigurable(deletable); + activation->__defineOwnProperty__(this, name, desc, attrs); +} + +String * const *ExecutionContext::formals() const +{ + return type >= Type_CallContext ? static_cast(this)->function->formalParameterList : 0; +} + +unsigned int ExecutionContext::formalCount() const +{ + return type >= Type_CallContext ? static_cast(this)->function->formalParameterCount : 0; +} + +String * const *ExecutionContext::variables() const +{ + return type >= Type_CallContext ? static_cast(this)->function->varList : 0; +} + +unsigned int ExecutionContext::variableCount() const +{ + return type >= Type_CallContext ? static_cast(this)->function->varCount : 0; +} + + +void GlobalContext::initGlobalContext(ExecutionEngine *eng) +{ + initBaseContext(Type_GlobalContext, eng, /*parentContext*/0); + thisObject = Value::fromObject(eng->globalObject); + global = 0; +} + +void WithContext::initWithContext(ExecutionContext *p, Object *with) +{ + initBaseContext(Type_WithContext, p->engine, p); + thisObject = p->thisObject; + outer = p; + lookups = p->lookups; + + withObject = with; +} + +void CatchContext::initCatchContext(ExecutionContext *p, String *exceptionVarName, const Value &exceptionValue) +{ + initBaseContext(Type_CatchContext, p->engine, p); + strictMode = p->strictMode; + thisObject = p->thisObject; + outer = p; + lookups = p->lookups; + + this->exceptionVarName = exceptionVarName; + this->exceptionValue = exceptionValue; +} + +void CallContext::initCallContext(ExecutionContext *parentContext, FunctionObject *function, Value *_arguments, int _argumentCount, const Value &_thisObject) +{ + initBaseContext(Type_CallContext, parentContext->engine, parentContext); + + this->function = function; + this->arguments = _arguments; + this->argumentCount = _argumentCount; + this->thisObject = _thisObject; + + strictMode = function->strictMode; + marked = false; + outer = function->scope; +#ifndef QT_NO_DEBUG + assert(outer->next != (ExecutionContext *)0x1); +#endif + + activation = 0; + + if (function->function) + lookups = function->function->lookups; + + uint argc = argumentCount; + + locals = (Value *)(this + 1); + if (function->varCount) + std::fill(locals, locals + function->varCount, Value::undefinedValue()); + + if (needsOwnArguments()) { + Value *args = arguments; + argumentCount = qMax(argc, function->formalParameterCount); + arguments = locals + function->varCount; + if (argc) + ::memcpy(arguments, args, argc * sizeof(Value)); + if (argc < function->formalParameterCount) + std::fill(arguments + argc, arguments + function->formalParameterCount, Value::undefinedValue()); + + } + + if (function->usesArgumentsObject) { + ArgumentsObject *args = new (engine->memoryManager) ArgumentsObject(this, function->formalParameterCount, argc); + args->prototype = engine->objectPrototype; + activation = engine->newObject(); + Property desc = Property::fromValue(Value::fromObject(args)); + activation->__defineOwnProperty__(this, engine->id_arguments, desc, Attr_NotConfigurable); + } +} + +void CallContext::initQmlContext(ExecutionContext *parentContext, Object *qml, FunctionObject *function) +{ + initBaseContext(Type_QmlContext, parentContext->engine, parentContext); + + this->function = function; + this->arguments = 0; + this->argumentCount = 0; + this->thisObject = Value::undefinedValue(); + + strictMode = true; + marked = false; + this->outer = function->scope; +#ifndef QT_NO_DEBUG + assert(outer->next != (ExecutionContext *)0x1); +#endif + + activation = qml; + + lookups = function->function->lookups; + + locals = (Value *)(this + 1); + if (function->varCount) + std::fill(locals, locals + function->varCount, Value::undefinedValue()); +} + + +bool ExecutionContext::deleteProperty(String *name) +{ + bool hasWith = false; + for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer) { + if (ctx->type == Type_WithContext) { + hasWith = true; + WithContext *w = static_cast(ctx); + if (w->withObject->__hasProperty__(name)) + return w->withObject->deleteProperty(name); + } else if (ctx->type == Type_CatchContext) { + CatchContext *c = static_cast(ctx); + if (c->exceptionVarName->isEqualTo(name)) + return false; + } else if (ctx->type >= Type_CallContext) { + CallContext *c = static_cast(ctx); + FunctionObject *f = c->function; + if (f->needsActivation || hasWith) { + for (unsigned int i = 0; i < f->varCount; ++i) + if (f->varList[i]->isEqualTo(name)) + return false; + for (int i = (int)f->formalParameterCount - 1; i >= 0; --i) + if (f->formalParameterList[i]->isEqualTo(name)) + return false; + } + if (c->activation && c->activation->__hasProperty__(name)) + return c->activation->deleteProperty(name); + } else if (ctx->type == Type_GlobalContext) { + GlobalContext *g = static_cast(ctx); + if (g->global->__hasProperty__(name)) + return g->global->deleteProperty(name); + } + } + + if (strictMode) + throwSyntaxError(0); + return true; +} + +bool CallContext::needsOwnArguments() const +{ + return function->needsActivation || argumentCount < function->formalParameterCount; +} + +void ExecutionContext::mark() +{ + if (marked) + return; + marked = true; + + if (type != Type_SimpleCallContext && outer) + outer->mark(); + + thisObject.mark(); + + if (type >= Type_SimpleCallContext) { + QV4::CallContext *c = static_cast(this); + for (unsigned arg = 0, lastArg = c->argumentCount; arg < lastArg; ++arg) + c->arguments[arg].mark(); + if (type >= Type_CallContext) { + for (unsigned local = 0, lastLocal = c->variableCount(); local < lastLocal; ++local) + c->locals[local].mark(); + if (c->activation) + c->activation->mark(); + c->function->mark(); + } + } else if (type == Type_WithContext) { + WithContext *w = static_cast(this); + w->withObject->mark(); + } else if (type == Type_CatchContext) { + CatchContext *c = static_cast(this); + if (c->exceptionVarName) + c->exceptionVarName->mark(); + c->exceptionValue.mark(); + } else if (type == Type_GlobalContext) { + GlobalContext *g = static_cast(this); + g->global->mark(); + } +} + +void ExecutionContext::setProperty(String *name, const Value& value) +{ + for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer) { + if (ctx->type == Type_WithContext) { + Object *w = static_cast(ctx)->withObject; + if (w->__hasProperty__(name)) { + w->put(name, value); + return; + } + } else if (ctx->type == Type_CatchContext && static_cast(ctx)->exceptionVarName->isEqualTo(name)) { + static_cast(ctx)->exceptionValue = value; + return; + } else { + Object *activation = 0; + if (ctx->type >= Type_CallContext) { + CallContext *c = static_cast(ctx); + for (unsigned int i = 0; i < c->function->varCount; ++i) + if (c->function->varList[i]->isEqualTo(name)) { + c->locals[i] = value; + return; + } + for (int i = (int)c->function->formalParameterCount - 1; i >= 0; --i) + if (c->function->formalParameterList[i]->isEqualTo(name)) { + c->arguments[i] = value; + return; + } + activation = c->activation; + } else if (ctx->type == Type_GlobalContext) { + activation = static_cast(ctx)->global; + } + + if (activation && (ctx->type == Type_QmlContext || activation->__hasProperty__(name))) { + activation->put(name, value); + return; + } + } + } + if (strictMode || name->isEqualTo(engine->id_this)) + throwReferenceError(Value::fromString(name)); + engine->globalObject->put(name, value); +} + +Value ExecutionContext::getProperty(String *name) +{ + name->makeIdentifier(); + + if (name->isEqualTo(engine->id_this)) + return thisObject; + + bool hasWith = false; + bool hasCatchScope = false; + for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer) { + if (ctx->type == Type_WithContext) { + Object *w = static_cast(ctx)->withObject; + hasWith = true; + bool hasProperty = false; + Value v = w->get(name, &hasProperty); + if (hasProperty) { + return v; + } + continue; + } + + else if (ctx->type == Type_CatchContext) { + hasCatchScope = true; + CatchContext *c = static_cast(ctx); + if (c->exceptionVarName->isEqualTo(name)) + return c->exceptionValue; + } + + else if (ctx->type >= Type_CallContext) { + QV4::CallContext *c = static_cast(ctx); + FunctionObject *f = c->function; + if (f->needsActivation || hasWith || hasCatchScope) { + for (unsigned int i = 0; i < f->varCount; ++i) + if (f->varList[i]->isEqualTo(name)) + return c->locals[i]; + for (int i = (int)f->formalParameterCount - 1; i >= 0; --i) + if (f->formalParameterList[i]->isEqualTo(name)) + return c->arguments[i]; + } + if (c->activation) { + bool hasProperty = false; + Value v = c->activation->get(name, &hasProperty); + if (hasProperty) + return v; + } + if (f->function && f->function->isNamedExpression + && name->isEqualTo(f->function->name)) + return Value::fromObject(c->function); + } + + else if (ctx->type == Type_GlobalContext) { + GlobalContext *g = static_cast(ctx); + bool hasProperty = false; + Value v = g->global->get(name, &hasProperty); + if (hasProperty) + return v; + } + } + throwReferenceError(Value::fromString(name)); + return Value::undefinedValue(); +} + +Value ExecutionContext::getPropertyNoThrow(String *name) +{ + name->makeIdentifier(); + + if (name->isEqualTo(engine->id_this)) + return thisObject; + + bool hasWith = false; + bool hasCatchScope = false; + for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer) { + if (ctx->type == Type_WithContext) { + Object *w = static_cast(ctx)->withObject; + hasWith = true; + bool hasProperty = false; + Value v = w->get(name, &hasProperty); + if (hasProperty) { + return v; + } + continue; + } + + else if (ctx->type == Type_CatchContext) { + hasCatchScope = true; + CatchContext *c = static_cast(ctx); + if (c->exceptionVarName->isEqualTo(name)) + return c->exceptionValue; + } + + else if (ctx->type >= Type_CallContext) { + QV4::CallContext *c = static_cast(ctx); + FunctionObject *f = c->function; + if (f->needsActivation || hasWith || hasCatchScope) { + for (unsigned int i = 0; i < f->varCount; ++i) + if (f->varList[i]->isEqualTo(name)) + return c->locals[i]; + for (int i = (int)f->formalParameterCount - 1; i >= 0; --i) + if (f->formalParameterList[i]->isEqualTo(name)) + return c->arguments[i]; + } + if (c->activation) { + bool hasProperty = false; + Value v = c->activation->get(name, &hasProperty); + if (hasProperty) + return v; + } + if (f->function && f->function->isNamedExpression + && name->isEqualTo(f->function->name)) + return Value::fromObject(c->function); + } + + else if (ctx->type == Type_GlobalContext) { + GlobalContext *g = static_cast(ctx); + bool hasProperty = false; + Value v = g->global->get(name, &hasProperty); + if (hasProperty) + return v; + } + } + return Value::undefinedValue(); +} + +Value ExecutionContext::getPropertyAndBase(String *name, Object **base) +{ + *base = 0; + name->makeIdentifier(); + + if (name->isEqualTo(engine->id_this)) + return thisObject; + + bool hasWith = false; + bool hasCatchScope = false; + for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer) { + if (ctx->type == Type_WithContext) { + Object *w = static_cast(ctx)->withObject; + hasWith = true; + bool hasProperty = false; + Value v = w->get(name, &hasProperty); + if (hasProperty) { + *base = w; + return v; + } + continue; + } + + else if (ctx->type == Type_CatchContext) { + hasCatchScope = true; + CatchContext *c = static_cast(ctx); + if (c->exceptionVarName->isEqualTo(name)) + return c->exceptionValue; + } + + else if (ctx->type >= Type_CallContext) { + QV4::CallContext *c = static_cast(ctx); + FunctionObject *f = c->function; + if (f->needsActivation || hasWith || hasCatchScope) { + for (unsigned int i = 0; i < f->varCount; ++i) + if (f->varList[i]->isEqualTo(name)) + return c->locals[i]; + for (int i = (int)f->formalParameterCount - 1; i >= 0; --i) + if (f->formalParameterList[i]->isEqualTo(name)) + return c->arguments[i]; + } + if (c->activation) { + bool hasProperty = false; + Value v = c->activation->get(name, &hasProperty); + if (hasProperty) { + *base = c->activation; + return v; + } + } + if (f->function && f->function->isNamedExpression + && name->isEqualTo(f->function->name)) + return Value::fromObject(c->function); + } + + else if (ctx->type == Type_GlobalContext) { + GlobalContext *g = static_cast(ctx); + bool hasProperty = false; + Value v = g->global->get(name, &hasProperty); + if (hasProperty) + return v; + } + } + throwReferenceError(Value::fromString(name)); + return Value::undefinedValue(); +} + + + +void ExecutionContext::inplaceBitOp(String *name, const Value &value, BinOp op) +{ + Value lhs = getProperty(name); + Value result; + op(&result, lhs, value); + setProperty(name, result); +} + +void ExecutionContext::throwError(const Value &value) +{ + __qmljs_throw(this, value); +} + +void ExecutionContext::throwError(const QString &message) +{ + Value v = Value::fromString(this, message); + throwError(Value::fromObject(engine->newErrorObject(v))); +} + +void ExecutionContext::throwSyntaxError(DiagnosticMessage *message) +{ + throwError(Value::fromObject(engine->newSyntaxErrorObject(this, message))); +} + +void ExecutionContext::throwTypeError() +{ + throwError(Value::fromObject(engine->newTypeErrorObject(QStringLiteral("Type error")))); +} + +void ExecutionContext::throwTypeError(const QString &message) +{ + throwError(Value::fromObject(engine->newTypeErrorObject(message))); +} + +void ExecutionContext::throwUnimplemented(const QString &message) +{ + Value v = Value::fromString(this, QStringLiteral("Unimplemented ") + message); + throwError(Value::fromObject(engine->newErrorObject(v))); +} + +void ExecutionContext::throwReferenceError(Value value) +{ + String *s = value.toString(this); + QString msg = s->toQString() + QStringLiteral(" is not defined"); + throwError(Value::fromObject(engine->newReferenceErrorObject(msg))); +} + +void ExecutionContext::throwReferenceError(Value value, const QString &fileName, int line) +{ + String *s = value.toString(this); + QString msg = s->toQString() + QStringLiteral(" is not defined"); + throwError(Value::fromObject(engine->newReferenceErrorObject(msg, fileName, line))); +} + +void ExecutionContext::throwRangeError(Value value) +{ + String *s = value.toString(this); + QString msg = s->toQString() + QStringLiteral(" out of range"); + throwError(Value::fromObject(engine->newRangeErrorObject(msg))); +} + +void ExecutionContext::throwURIError(Value msg) +{ + throwError(Value::fromObject(engine->newURIErrorObject(msg))); +} + + +void SimpleCallContext::initSimpleCallContext(ExecutionEngine *engine) +{ + initBaseContext(Type_SimpleCallContext, engine, engine->current); + function = 0; + arguments = 0; + argumentCount = 0; +} diff --git a/src/qml/jsruntime/qv4context_p.h b/src/qml/jsruntime/qv4context_p.h new file mode 100644 index 0000000000..dfe02bdcc8 --- /dev/null +++ b/src/qml/jsruntime/qv4context_p.h @@ -0,0 +1,227 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QMLJS_ENVIRONMENT_H +#define QMLJS_ENVIRONMENT_H + +#include "qv4global_p.h" +#include "qv4runtime_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct Object; +struct ExecutionEngine; +struct DeclarativeEnvironment; +struct Lookup; + +struct Q_QML_EXPORT DiagnosticMessage +{ + enum { Error, Warning }; + + QString fileName; + quint32 offset; + quint32 length; + quint32 startLine; + unsigned startColumn: 31; + unsigned type: 1; + QString message; + DiagnosticMessage *next; + + DiagnosticMessage(); + ~DiagnosticMessage(); + String *buildFullMessage(ExecutionContext *ctx) const; +}; + +struct CallContext; + +struct Q_QML_EXPORT ExecutionContext +{ + enum Type { + Type_GlobalContext = 0x1, + Type_CatchContext = 0x2, + Type_WithContext = 0x3, + Type_SimpleCallContext = 0x4, + Type_CallContext = 0x5, + Type_QmlContext = 0x6 + }; + + Type type; + bool strictMode; + bool marked; + + Value thisObject; + + ExecutionEngine *engine; + ExecutionContext *parent; + ExecutionContext *outer; + Lookup *lookups; + ExecutionContext *next; // used in the GC + + struct EvalCode + { + Function *function; + EvalCode *next; + }; + EvalCode *currentEvalCode; + + const uchar **interpreterInstructionPointer; + + void initBaseContext(Type type, ExecutionEngine *engine, ExecutionContext *parentContext) + { + this->type = type; + strictMode = false; + marked = false; + thisObject = Value::undefinedValue(); + this->engine = engine; + parent = parentContext; + outer = 0; + lookups = 0; + currentEvalCode = 0; + interpreterInstructionPointer = 0; + } + + String * const *formals() const; + unsigned int formalCount() const; + String * const *variables() const; + unsigned int variableCount() const; + + void createMutableBinding(String *name, bool deletable); + + void Q_NORETURN throwError(const Value &value); + void Q_NORETURN throwError(const QString &message); + void Q_NORETURN throwSyntaxError(DiagnosticMessage *message); + void Q_NORETURN throwTypeError(); + void Q_NORETURN throwTypeError(const QString &message); + void Q_NORETURN throwReferenceError(Value value); + void Q_NORETURN throwReferenceError(Value value, const QString &fileName, int line); + void Q_NORETURN throwRangeError(Value value); + void Q_NORETURN throwURIError(Value msg); + void Q_NORETURN throwUnimplemented(const QString &message); + + void setProperty(String *name, const Value &value); + Value getProperty(String *name); + Value getPropertyNoThrow(String *name); + Value getPropertyAndBase(String *name, Object **base); + void inplaceBitOp(String *name, const QV4::Value &value, BinOp op); + bool deleteProperty(String *name); + + inline Value argument(unsigned int index = 0); + + void mark(); + + inline CallContext *asCallContext(); + inline const CallContext *asCallContext() const; +}; + +struct SimpleCallContext : public ExecutionContext +{ + void initSimpleCallContext(ExecutionEngine *engine); + FunctionObject *function; + Value *arguments; + unsigned int argumentCount; +}; + +struct CallContext : public SimpleCallContext +{ + void initCallContext(ExecutionContext *parentContext, FunctionObject *function, Value *args, int argc, + const Value &thisObject); + void initQmlContext(ExecutionContext *parentContext, Object *qml, QV4::FunctionObject *function); + bool needsOwnArguments() const; + + Value *locals; + Object *activation; +}; + +struct GlobalContext : public ExecutionContext +{ + void initGlobalContext(ExecutionEngine *e); + + Object *global; +}; + +struct CatchContext : public ExecutionContext +{ + void initCatchContext(ExecutionContext *p, String *exceptionVarName, const QV4::Value &exceptionValue); + + String *exceptionVarName; + Value exceptionValue; +}; + +struct WithContext : public ExecutionContext +{ + Object *withObject; + + void initWithContext(ExecutionContext *p, Object *with); +}; + +inline Value ExecutionContext::argument(unsigned int index) +{ + if (type >= Type_SimpleCallContext) { + CallContext *ctx = static_cast(this); + if (index < ctx->argumentCount) + return ctx->arguments[index]; + } + return Value::undefinedValue(); +} + +inline CallContext *ExecutionContext::asCallContext() +{ + return type >= Type_CallContext ? static_cast(this) : 0; +} + +inline const CallContext *ExecutionContext::asCallContext() const +{ + return type >= Type_CallContext ? static_cast(this) : 0; +} + +/* Function *f, int argc */ +#define requiredMemoryForExecutionContect(f, argc) \ + sizeof(CallContext) + sizeof(Value) * (f->varCount + qMax((uint)argc, f->formalParameterCount)) +#define requiredMemoryForQmlExecutionContect(f) \ + sizeof(CallContext) + sizeof(Value) * (f->locals.size()) +#define stackContextSize (sizeof(CallContext) + 32*sizeof(Value)) + +} // namespace QV4 + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/jsruntime/qv4dateobject.cpp b/src/qml/jsruntime/qv4dateobject.cpp new file mode 100644 index 0000000000..3cf6cb1aeb --- /dev/null +++ b/src/qml/jsruntime/qv4dateobject.cpp @@ -0,0 +1,1316 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qv4dateobject_p.h" +#include "qv4objectproto_p.h" +#include "qv4mm_p.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifdef Q_OS_WIN +# include +#else +# ifndef Q_OS_VXWORKS +# include +# else +# include "qplatformdefs.h" +# endif +#endif + +using namespace QV4; + +static const double HoursPerDay = 24.0; +static const double MinutesPerHour = 60.0; +static const double SecondsPerMinute = 60.0; +static const double msPerSecond = 1000.0; +static const double msPerMinute = 60000.0; +static const double msPerHour = 3600000.0; +static const double msPerDay = 86400000.0; + +static double LocalTZA = 0.0; // initialized at startup + +static inline double TimeWithinDay(double t) +{ + double r = ::fmod(t, msPerDay); + return (r >= 0) ? r : r + msPerDay; +} + +static inline int HourFromTime(double t) +{ + int r = int(::fmod(::floor(t / msPerHour), HoursPerDay)); + return (r >= 0) ? r : r + int(HoursPerDay); +} + +static inline int MinFromTime(double t) +{ + int r = int(::fmod(::floor(t / msPerMinute), MinutesPerHour)); + return (r >= 0) ? r : r + int(MinutesPerHour); +} + +static inline int SecFromTime(double t) +{ + int r = int(::fmod(::floor(t / msPerSecond), SecondsPerMinute)); + return (r >= 0) ? r : r + int(SecondsPerMinute); +} + +static inline int msFromTime(double t) +{ + int r = int(::fmod(t, msPerSecond)); + return (r >= 0) ? r : r + int(msPerSecond); +} + +static inline double Day(double t) +{ + return ::floor(t / msPerDay); +} + +static inline double DaysInYear(double y) +{ + if (::fmod(y, 4)) + return 365; + + else if (::fmod(y, 100)) + return 366; + + else if (::fmod(y, 400)) + return 365; + + return 366; +} + +static inline double DayFromYear(double y) +{ + return 365 * (y - 1970) + + ::floor((y - 1969) / 4) + - ::floor((y - 1901) / 100) + + ::floor((y - 1601) / 400); +} + +static inline double TimeFromYear(double y) +{ + return msPerDay * DayFromYear(y); +} + +static inline double YearFromTime(double t) +{ + int y = 1970; + y += (int) ::floor(t / (msPerDay * 365.2425)); + + double t2 = TimeFromYear(y); + return (t2 > t) ? y - 1 : ((t2 + msPerDay * DaysInYear(y)) <= t) ? y + 1 : y; +} + +static inline bool InLeapYear(double t) +{ + double x = DaysInYear(YearFromTime(t)); + if (x == 365) + return 0; + + assert(x == 366); + return 1; +} + +static inline double DayWithinYear(double t) +{ + return Day(t) - DayFromYear(YearFromTime(t)); +} + +static inline double MonthFromTime(double t) +{ + double d = DayWithinYear(t); + double l = InLeapYear(t); + + if (d < 31.0) + return 0; + + else if (d < 59.0 + l) + return 1; + + else if (d < 90.0 + l) + return 2; + + else if (d < 120.0 + l) + return 3; + + else if (d < 151.0 + l) + return 4; + + else if (d < 181.0 + l) + return 5; + + else if (d < 212.0 + l) + return 6; + + else if (d < 243.0 + l) + return 7; + + else if (d < 273.0 + l) + return 8; + + else if (d < 304.0 + l) + return 9; + + else if (d < 334.0 + l) + return 10; + + else if (d < 365.0 + l) + return 11; + + return qSNaN(); // ### assert? +} + +static inline double DateFromTime(double t) +{ + int m = (int) Value::toInteger(MonthFromTime(t)); + double d = DayWithinYear(t); + double l = InLeapYear(t); + + switch (m) { + case 0: return d + 1.0; + case 1: return d - 30.0; + case 2: return d - 58.0 - l; + case 3: return d - 89.0 - l; + case 4: return d - 119.0 - l; + case 5: return d - 150.0 - l; + case 6: return d - 180.0 - l; + case 7: return d - 211.0 - l; + case 8: return d - 242.0 - l; + case 9: return d - 272.0 - l; + case 10: return d - 303.0 - l; + case 11: return d - 333.0 - l; + } + + return qSNaN(); // ### assert +} + +static inline double WeekDay(double t) +{ + double r = ::fmod (Day(t) + 4.0, 7.0); + return (r >= 0) ? r : r + 7.0; +} + + +static inline double MakeTime(double hour, double min, double sec, double ms) +{ + return ((hour * MinutesPerHour + min) * SecondsPerMinute + sec) * msPerSecond + ms; +} + +static inline double DayFromMonth(double month, double leap) +{ + switch ((int) month) { + case 0: return 0; + case 1: return 31.0; + case 2: return 59.0 + leap; + case 3: return 90.0 + leap; + case 4: return 120.0 + leap; + case 5: return 151.0 + leap; + case 6: return 181.0 + leap; + case 7: return 212.0 + leap; + case 8: return 243.0 + leap; + case 9: return 273.0 + leap; + case 10: return 304.0 + leap; + case 11: return 334.0 + leap; + } + + return qSNaN(); // ### assert? +} + +static double MakeDay(double year, double month, double day) +{ + year += ::floor(month / 12.0); + + month = ::fmod(month, 12.0); + if (month < 0) + month += 12.0; + + double d = DayFromYear(year); + bool leap = InLeapYear(d*msPerDay); + + d += DayFromMonth(month, leap); + d += day - 1; + + return d; +} + +static inline double MakeDate(double day, double time) +{ + return day * msPerDay + time; +} + +static inline double DaylightSavingTA(double t) +{ + struct tm tmtm; +#if defined(_MSC_VER) && _MSC_VER >= 1400 + __time64_t tt = (__time64_t)(t / msPerSecond); + // _localtime_64_s returns non-zero on failure + if (_localtime64_s(&tmtm, &tt) != 0) +#else + long int tt = (long int)(t / msPerSecond); + if (!localtime_r((const time_t*) &tt, &tmtm)) +#endif + return 0; + return (tmtm.tm_isdst > 0) ? msPerHour : 0; +} + +static inline double LocalTime(double t) +{ + return t + LocalTZA + DaylightSavingTA(t); +} + +static inline double UTC(double t) +{ + return t - LocalTZA - DaylightSavingTA(t - LocalTZA); +} + +static inline double currentTime() +{ +#ifndef Q_OS_WIN + struct timeval tv; + + gettimeofday(&tv, 0); + return ::floor(tv.tv_sec * msPerSecond + (tv.tv_usec / 1000.0)); +#else + SYSTEMTIME st; + GetSystemTime(&st); + FILETIME ft; + SystemTimeToFileTime(&st, &ft); + LARGE_INTEGER li; + li.LowPart = ft.dwLowDateTime; + li.HighPart = ft.dwHighDateTime; + return double(li.QuadPart - Q_INT64_C(116444736000000000)) / 10000.0; +#endif +} + +static inline double TimeClip(double t) +{ + if (! qIsFinite(t) || fabs(t) > 8.64e15) + return qSNaN(); + return Value::toInteger(t); +} + +static inline double ParseString(const QString &s) +{ + // first try the format defined in 15.9.1.15, only if that fails fall back to + // QDateTime for parsing + + // the define string format is YYYY-MM-DDTHH:mm:ss.sssZ + // It can be date or time only, and the second and later components + // of both fields are optional + // and extended syntax for negative and large positive years exists: +/-YYYYYY + + enum Format { + Year, + Month, + Day, + Hour, + Minute, + Second, + MilliSecond, + TimezoneHour, + TimezoneMinute, + Done + }; + + const QChar *ch = s.constData(); + const QChar *end = ch + s.length(); + + uint format = Year; + int current = 0; + int currentSize = 0; + bool extendedYear = false; + + int yearSign = 1; + int year = 0; + int month = 0; + int day = 1; + int hour = 0; + int minute = 0; + int second = 0; + int msec = 0; + int offsetSign = 1; + int offset = 0; + + bool error = false; + if (*ch == '+' || *ch == '-') { + extendedYear = true; + if (*ch == '-') + yearSign = -1; + ++ch; + } + while (ch <= end) { + if (*ch >= '0' && *ch <= '9') { + current *= 10; + current += ch->unicode() - '0'; + ++currentSize; + } else { // other char, delimits field + switch (format) { + case Year: + year = current; + if (extendedYear) + error = (currentSize != 6); + else + error = (currentSize != 4); + break; + case Month: + month = current - 1; + error = (currentSize != 2) || month > 11; + break; + case Day: + day = current; + error = (currentSize != 2) || day > 31; + break; + case Hour: + hour = current; + error = (currentSize != 2) || hour > 24; + break; + case Minute: + minute = current; + error = (currentSize != 2) || minute > 60; + break; + case Second: + second = current; + error = (currentSize != 2) || second > 60; + break; + case MilliSecond: + msec = current; + error = (currentSize != 3); + break; + case TimezoneHour: + offset = current*60; + error = (currentSize != 2) || offset > 23*60; + break; + case TimezoneMinute: + offset += current; + error = (currentSize != 2) || current >= 60; + break; + } + if (*ch == 'T') { + if (format >= Hour) + error = true; + format = Hour; + } else if (*ch == '-') { + if (format < Day) + ++format; + else if (format < Minute) + error = true; + else if (format >= TimezoneHour) + error = true; + else { + offsetSign = -1; + format = TimezoneHour; + } + } else if (*ch == ':') { + if (format != Hour && format != Minute && format != TimezoneHour) + error = true; + ++format; + } else if (*ch == '.') { + if (format != Second) + error = true; + ++format; + } else if (*ch == '+') { + if (format < Minute || format >= TimezoneHour) + error = true; + format = TimezoneHour; + } else if (*ch == 'Z' || *ch == 0) { + format = Done; + } + current = 0; + currentSize = 0; + } + if (error || format == Done) + break; + ++ch; + } + + if (!error) { + double t = MakeDate(MakeDay(year * yearSign, month, day), MakeTime(hour, minute, second, msec)); + t -= offset * offsetSign * 60 * 1000; + return t; + } + + QDateTime dt = QDateTime::fromString(s, Qt::TextDate); + if (!dt.isValid()) + dt = QDateTime::fromString(s, Qt::ISODate); + if (!dt.isValid()) { + QStringList formats; + formats << QStringLiteral("M/d/yyyy") + << QStringLiteral("M/d/yyyy hh:mm") + << QStringLiteral("M/d/yyyy hh:mm A") + + << QStringLiteral("M/d/yyyy, hh:mm") + << QStringLiteral("M/d/yyyy, hh:mm A") + + << QStringLiteral("MMM d yyyy") + << QStringLiteral("MMM d yyyy hh:mm") + << QStringLiteral("MMM d yyyy hh:mm:ss") + << QStringLiteral("MMM d yyyy, hh:mm") + << QStringLiteral("MMM d yyyy, hh:mm:ss") + + << QStringLiteral("MMMM d yyyy") + << QStringLiteral("MMMM d yyyy hh:mm") + << QStringLiteral("MMMM d yyyy hh:mm:ss") + << QStringLiteral("MMMM d yyyy, hh:mm") + << QStringLiteral("MMMM d yyyy, hh:mm:ss") + + << QStringLiteral("MMM d, yyyy") + << QStringLiteral("MMM d, yyyy hh:mm") + << QStringLiteral("MMM d, yyyy hh:mm:ss") + + << QStringLiteral("MMMM d, yyyy") + << QStringLiteral("MMMM d, yyyy hh:mm") + << QStringLiteral("MMMM d, yyyy hh:mm:ss") + + << QStringLiteral("d MMM yyyy") + << QStringLiteral("d MMM yyyy hh:mm") + << QStringLiteral("d MMM yyyy hh:mm:ss") + << QStringLiteral("d MMM yyyy, hh:mm") + << QStringLiteral("d MMM yyyy, hh:mm:ss") + + << QStringLiteral("d MMMM yyyy") + << QStringLiteral("d MMMM yyyy hh:mm") + << QStringLiteral("d MMMM yyyy hh:mm:ss") + << QStringLiteral("d MMMM yyyy, hh:mm") + << QStringLiteral("d MMMM yyyy, hh:mm:ss") + + << QStringLiteral("d MMM, yyyy") + << QStringLiteral("d MMM, yyyy hh:mm") + << QStringLiteral("d MMM, yyyy hh:mm:ss") + + << QStringLiteral("d MMMM, yyyy") + << QStringLiteral("d MMMM, yyyy hh:mm") + << QStringLiteral("d MMMM, yyyy hh:mm:ss"); + + for (int i = 0; i < formats.size(); ++i) { + dt = QDateTime::fromString(s, formats.at(i)); + if (dt.isValid()) + break; + } + } + if (!dt.isValid()) + return qSNaN(); + return dt.toMSecsSinceEpoch(); +} + +/*! + \internal + + Converts the ECMA Date value \tt (in UTC form) to QDateTime + according to \a spec. +*/ +static inline QDateTime ToDateTime(double t, Qt::TimeSpec spec) +{ + if (std::isnan(t)) + return QDateTime(); + if (spec == Qt::LocalTime) + t = LocalTime(t); + int year = int(YearFromTime(t)); + int month = int(MonthFromTime(t) + 1); + int day = int(DateFromTime(t)); + int hours = HourFromTime(t); + int mins = MinFromTime(t); + int secs = SecFromTime(t); + int ms = msFromTime(t); + return QDateTime(QDate(year, month, day), QTime(hours, mins, secs, ms), spec); +} + +static inline QString ToString(double t) +{ + if (std::isnan(t)) + return QStringLiteral("Invalid Date"); + QString str = ToDateTime(t, Qt::LocalTime).toString() + QStringLiteral(" GMT"); + double tzoffset = LocalTZA + DaylightSavingTA(t); + if (tzoffset) { + int hours = static_cast(::fabs(tzoffset) / 1000 / 60 / 60); + int mins = int(::fabs(tzoffset) / 1000 / 60) % 60; + str.append(QLatin1Char((tzoffset > 0) ? '+' : '-')); + if (hours < 10) + str.append(QLatin1Char('0')); + str.append(QString::number(hours)); + if (mins < 10) + str.append(QLatin1Char('0')); + str.append(QString::number(mins)); + } + return str; +} + +static inline QString ToUTCString(double t) +{ + if (std::isnan(t)) + return QStringLiteral("Invalid Date"); + return ToDateTime(t, Qt::UTC).toString() + QStringLiteral(" GMT"); +} + +static inline QString ToDateString(double t) +{ + return ToDateTime(t, Qt::LocalTime).date().toString(); +} + +static inline QString ToTimeString(double t) +{ + return ToDateTime(t, Qt::LocalTime).time().toString(); +} + +static inline QString ToLocaleString(double t) +{ + return ToDateTime(t, Qt::LocalTime).toString(Qt::LocaleDate); +} + +static inline QString ToLocaleDateString(double t) +{ + return ToDateTime(t, Qt::LocalTime).date().toString(Qt::LocaleDate); +} + +static inline QString ToLocaleTimeString(double t) +{ + return ToDateTime(t, Qt::LocalTime).time().toString(Qt::LocaleDate); +} + +static double getLocalTZA() +{ +#ifndef Q_OS_WIN + struct tm t; + time_t curr; + time(&curr); + localtime_r(&curr, &t); + time_t locl = mktime(&t); + gmtime_r(&curr, &t); + time_t globl = mktime(&t); + return double(locl - globl) * 1000.0; +#else + TIME_ZONE_INFORMATION tzInfo; + GetTimeZoneInformation(&tzInfo); + return -tzInfo.Bias * 60.0 * 1000.0; +#endif +} + +DateObject::DateObject(ExecutionEngine *engine, const QDateTime &date) + : Object(engine) +{ + type = Type_DateObject; + value = Value::fromDouble(date.toMSecsSinceEpoch()); +} + +QDateTime DateObject::toQDateTime() const +{ + return ToDateTime(value.asDouble(), Qt::LocalTime); +} + +DEFINE_MANAGED_VTABLE(DateCtor); + +DateCtor::DateCtor(ExecutionContext *scope) + : FunctionObject(scope, scope->engine->newIdentifier(QStringLiteral("Date"))) +{ + vtbl = &static_vtbl; +} + +Value DateCtor::construct(Managed *m, Value *args, int argc) +{ + double t = 0; + + if (argc == 0) + t = currentTime(); + + else if (argc == 1) { + Value arg = args[0]; + if (DateObject *d = arg.asDateObject()) + arg = d->value; + else + arg = __qmljs_to_primitive(arg, PREFERREDTYPE_HINT); + + if (arg.isString()) + t = ParseString(arg.stringValue()->toQString()); + else + t = TimeClip(arg.toNumber()); + } + + else { // argc > 1 + double year = args[0].toNumber(); + double month = args[1].toNumber(); + double day = argc >= 3 ? args[2].toNumber() : 1; + double hours = argc >= 4 ? args[3].toNumber() : 0; + double mins = argc >= 5 ? args[4].toNumber() : 0; + double secs = argc >= 6 ? args[5].toNumber() : 0; + double ms = argc >= 7 ? args[6].toNumber() : 0; + if (year >= 0 && year <= 99) + year += 1900; + t = MakeDate(MakeDay(year, month, day), MakeTime(hours, mins, secs, ms)); + t = TimeClip(UTC(t)); + } + + Object *d = m->engine()->newDateObject(Value::fromDouble(t)); + return Value::fromObject(d); +} + +Value DateCtor::call(Managed *m, const Value &, Value *, int) +{ + double t = currentTime(); + return Value::fromString(m->engine()->current, ToString(t)); +} + +void DatePrototype::init(ExecutionContext *ctx, const Value &ctor) +{ + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(7)); + LocalTZA = getLocalTZA(); + + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("parse"), method_parse, 1); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("UTC"), method_UTC, 7); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("now"), method_now, 0); + + defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); + defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); + defineDefaultProperty(ctx, QStringLiteral("toDateString"), method_toDateString, 0); + defineDefaultProperty(ctx, QStringLiteral("toTimeString"), method_toTimeString, 0); + defineDefaultProperty(ctx, QStringLiteral("toLocaleString"), method_toLocaleString, 0); + defineDefaultProperty(ctx, QStringLiteral("toLocaleDateString"), method_toLocaleDateString, 0); + defineDefaultProperty(ctx, QStringLiteral("toLocaleTimeString"), method_toLocaleTimeString, 0); + defineDefaultProperty(ctx, QStringLiteral("valueOf"), method_valueOf, 0); + defineDefaultProperty(ctx, QStringLiteral("getTime"), method_getTime, 0); + defineDefaultProperty(ctx, QStringLiteral("getYear"), method_getYear, 0); + defineDefaultProperty(ctx, QStringLiteral("getFullYear"), method_getFullYear, 0); + defineDefaultProperty(ctx, QStringLiteral("getUTCFullYear"), method_getUTCFullYear, 0); + defineDefaultProperty(ctx, QStringLiteral("getMonth"), method_getMonth, 0); + defineDefaultProperty(ctx, QStringLiteral("getUTCMonth"), method_getUTCMonth, 0); + defineDefaultProperty(ctx, QStringLiteral("getDate"), method_getDate, 0); + defineDefaultProperty(ctx, QStringLiteral("getUTCDate"), method_getUTCDate, 0); + defineDefaultProperty(ctx, QStringLiteral("getDay"), method_getDay, 0); + defineDefaultProperty(ctx, QStringLiteral("getUTCDay"), method_getUTCDay, 0); + defineDefaultProperty(ctx, QStringLiteral("getHours"), method_getHours, 0); + defineDefaultProperty(ctx, QStringLiteral("getUTCHours"), method_getUTCHours, 0); + defineDefaultProperty(ctx, QStringLiteral("getMinutes"), method_getMinutes, 0); + defineDefaultProperty(ctx, QStringLiteral("getUTCMinutes"), method_getUTCMinutes, 0); + defineDefaultProperty(ctx, QStringLiteral("getSeconds"), method_getSeconds, 0); + defineDefaultProperty(ctx, QStringLiteral("getUTCSeconds"), method_getUTCSeconds, 0); + defineDefaultProperty(ctx, QStringLiteral("getMilliseconds"), method_getMilliseconds, 0); + defineDefaultProperty(ctx, QStringLiteral("getUTCMilliseconds"), method_getUTCMilliseconds, 0); + defineDefaultProperty(ctx, QStringLiteral("getTimezoneOffset"), method_getTimezoneOffset, 0); + defineDefaultProperty(ctx, QStringLiteral("setTime"), method_setTime, 1); + defineDefaultProperty(ctx, QStringLiteral("setMilliseconds"), method_setMilliseconds, 1); + defineDefaultProperty(ctx, QStringLiteral("setUTCMilliseconds"), method_setUTCMilliseconds, 1); + defineDefaultProperty(ctx, QStringLiteral("setSeconds"), method_setSeconds, 2); + defineDefaultProperty(ctx, QStringLiteral("setUTCSeconds"), method_setUTCSeconds, 2); + defineDefaultProperty(ctx, QStringLiteral("setMinutes"), method_setMinutes, 3); + defineDefaultProperty(ctx, QStringLiteral("setUTCMinutes"), method_setUTCMinutes, 3); + defineDefaultProperty(ctx, QStringLiteral("setHours"), method_setHours, 4); + defineDefaultProperty(ctx, QStringLiteral("setUTCHours"), method_setUTCHours, 4); + defineDefaultProperty(ctx, QStringLiteral("setDate"), method_setDate, 1); + defineDefaultProperty(ctx, QStringLiteral("setUTCDate"), method_setUTCDate, 1); + defineDefaultProperty(ctx, QStringLiteral("setMonth"), method_setMonth, 2); + defineDefaultProperty(ctx, QStringLiteral("setUTCMonth"), method_setUTCMonth, 2); + defineDefaultProperty(ctx, QStringLiteral("setYear"), method_setYear, 1); + defineDefaultProperty(ctx, QStringLiteral("setFullYear"), method_setFullYear, 3); + defineDefaultProperty(ctx, QStringLiteral("setUTCFullYear"), method_setUTCFullYear, 3); + defineDefaultProperty(ctx, QStringLiteral("toUTCString"), method_toUTCString, 0); + defineDefaultProperty(ctx, QStringLiteral("toGMTString"), method_toUTCString, 0); + defineDefaultProperty(ctx, QStringLiteral("toISOString"), method_toISOString, 0); + defineDefaultProperty(ctx, QStringLiteral("toJSON"), method_toJSON, 1); +} + +double DatePrototype::getThisDate(ExecutionContext *ctx) +{ + if (DateObject *thisObject = ctx->thisObject.asDateObject()) + return thisObject->value.asDouble(); + else { + ctx->throwTypeError(); + return 0; + } +} + +Value DatePrototype::method_parse(SimpleCallContext *ctx) +{ + return Value::fromDouble(ParseString(ctx->argument(0).toString(ctx)->toQString())); +} + +Value DatePrototype::method_UTC(SimpleCallContext *ctx) +{ + const int numArgs = ctx->argumentCount; + if (numArgs >= 2) { + double year = ctx->argument(0).toNumber(); + double month = ctx->argument(1).toNumber(); + double day = numArgs >= 3 ? ctx->argument(2).toNumber() : 1; + double hours = numArgs >= 4 ? ctx->argument(3).toNumber() : 0; + double mins = numArgs >= 5 ? ctx->argument(4).toNumber() : 0; + double secs = numArgs >= 6 ? ctx->argument(5).toNumber() : 0; + double ms = numArgs >= 7 ? ctx->argument(6).toNumber() : 0; + if (year >= 0 && year <= 99) + year += 1900; + double t = MakeDate(MakeDay(year, month, day), + MakeTime(hours, mins, secs, ms)); + return Value::fromDouble(TimeClip(t)); + } + return Value::undefinedValue(); +} + +Value DatePrototype::method_now(SimpleCallContext *ctx) +{ + Q_UNUSED(ctx); + double t = currentTime(); + return Value::fromDouble(t); +} + +Value DatePrototype::method_toString(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + return Value::fromString(ctx, ToString(t)); +} + +Value DatePrototype::method_toDateString(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + return Value::fromString(ctx, ToDateString(t)); +} + +Value DatePrototype::method_toTimeString(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + return Value::fromString(ctx, ToTimeString(t)); +} + +Value DatePrototype::method_toLocaleString(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + return Value::fromString(ctx, ToLocaleString(t)); +} + +Value DatePrototype::method_toLocaleDateString(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + return Value::fromString(ctx, ToLocaleDateString(t)); +} + +Value DatePrototype::method_toLocaleTimeString(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + return Value::fromString(ctx, ToLocaleTimeString(t)); +} + +Value DatePrototype::method_valueOf(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getTime(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getYear(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = YearFromTime(LocalTime(t)) - 1900; + return Value::fromDouble(t); +} + +Value DatePrototype::method_getFullYear(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = YearFromTime(LocalTime(t)); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getUTCFullYear(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = YearFromTime(t); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getMonth(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = MonthFromTime(LocalTime(t)); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getUTCMonth(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = MonthFromTime(t); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getDate(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = DateFromTime(LocalTime(t)); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getUTCDate(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = DateFromTime(t); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getDay(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = WeekDay(LocalTime(t)); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getUTCDay(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = WeekDay(t); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getHours(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = HourFromTime(LocalTime(t)); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getUTCHours(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = HourFromTime(t); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getMinutes(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = MinFromTime(LocalTime(t)); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getUTCMinutes(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = MinFromTime(t); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getSeconds(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = SecFromTime(LocalTime(t)); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getUTCSeconds(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = SecFromTime(t); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getMilliseconds(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = msFromTime(LocalTime(t)); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getUTCMilliseconds(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = msFromTime(t); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getTimezoneOffset(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = (t - LocalTime(t)) / msPerMinute; + return Value::fromDouble(t); +} + +Value DatePrototype::method_setTime(SimpleCallContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + self->value.setDouble(TimeClip(ctx->argument(0).toNumber())); + return self->value; +} + +Value DatePrototype::method_setMilliseconds(SimpleCallContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = LocalTime(self->value.asDouble()); + double ms = ctx->argument(0).toNumber(); + self->value.setDouble(TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms))))); + return self->value; +} + +Value DatePrototype::method_setUTCMilliseconds(SimpleCallContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = self->value.asDouble(); + double ms = ctx->argument(0).toNumber(); + self->value.setDouble(TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms))))); + return self->value; +} + +Value DatePrototype::method_setSeconds(SimpleCallContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = LocalTime(self->value.asDouble()); + double sec = ctx->argument(0).toNumber(); + double ms = (ctx->argumentCount < 2) ? msFromTime(t) : ctx->argument(1).toNumber(); + t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms)))); + self->value.setDouble(t); + return self->value; +} + +Value DatePrototype::method_setUTCSeconds(SimpleCallContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = self->value.asDouble(); + double sec = ctx->argument(0).toNumber(); + double ms = (ctx->argumentCount < 2) ? msFromTime(t) : ctx->argument(1).toNumber(); + t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms)))); + self->value.setDouble(t); + return self->value; +} + +Value DatePrototype::method_setMinutes(SimpleCallContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = LocalTime(self->value.asDouble()); + double min = ctx->argument(0).toNumber(); + double sec = (ctx->argumentCount < 2) ? SecFromTime(t) : ctx->argument(1).toNumber(); + double ms = (ctx->argumentCount < 3) ? msFromTime(t) : ctx->argument(2).toNumber(); + t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), min, sec, ms)))); + self->value.setDouble(t); + return self->value; +} + +Value DatePrototype::method_setUTCMinutes(SimpleCallContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = self->value.asDouble(); + double min = ctx->argument(0).toNumber(); + double sec = (ctx->argumentCount < 2) ? SecFromTime(t) : ctx->argument(1).toNumber(); + double ms = (ctx->argumentCount < 3) ? msFromTime(t) : ctx->argument(2).toNumber(); + t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), min, sec, ms)))); + self->value.setDouble(t); + return self->value; +} + +Value DatePrototype::method_setHours(SimpleCallContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = LocalTime(self->value.asDouble()); + double hour = ctx->argument(0).toNumber(); + double min = (ctx->argumentCount < 2) ? MinFromTime(t) : ctx->argument(1).toNumber(); + double sec = (ctx->argumentCount < 3) ? SecFromTime(t) : ctx->argument(2).toNumber(); + double ms = (ctx->argumentCount < 4) ? msFromTime(t) : ctx->argument(3).toNumber(); + t = TimeClip(UTC(MakeDate(Day(t), MakeTime(hour, min, sec, ms)))); + self->value.setDouble(t); + return self->value; +} + +Value DatePrototype::method_setUTCHours(SimpleCallContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = self->value.asDouble(); + double hour = ctx->argument(0).toNumber(); + double min = (ctx->argumentCount < 2) ? MinFromTime(t) : ctx->argument(1).toNumber(); + double sec = (ctx->argumentCount < 3) ? SecFromTime(t) : ctx->argument(2).toNumber(); + double ms = (ctx->argumentCount < 4) ? msFromTime(t) : ctx->argument(3).toNumber(); + t = TimeClip(UTC(MakeDate(Day(t), MakeTime(hour, min, sec, ms)))); + self->value.setDouble(t); + return self->value; +} + +Value DatePrototype::method_setDate(SimpleCallContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = LocalTime(self->value.asDouble()); + double date = ctx->argument(0).toNumber(); + t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t)))); + self->value.setDouble(t); + return self->value; +} + +Value DatePrototype::method_setUTCDate(SimpleCallContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = self->value.asDouble(); + double date = ctx->argument(0).toNumber(); + t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t)))); + self->value.setDouble(t); + return self->value; +} + +Value DatePrototype::method_setMonth(SimpleCallContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = LocalTime(self->value.asDouble()); + double month = ctx->argument(0).toNumber(); + double date = (ctx->argumentCount < 2) ? DateFromTime(t) : ctx->argument(1).toNumber(); + t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t)))); + self->value.setDouble(t); + return self->value; +} + +Value DatePrototype::method_setUTCMonth(SimpleCallContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = self->value.asDouble(); + double month = ctx->argument(0).toNumber(); + double date = (ctx->argumentCount < 2) ? DateFromTime(t) : ctx->argument(1).toNumber(); + t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t)))); + self->value.setDouble(t); + return self->value; +} + +Value DatePrototype::method_setYear(SimpleCallContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = self->value.asDouble(); + if (std::isnan(t)) + t = 0; + else + t = LocalTime(t); + double year = ctx->argument(0).toNumber(); + double r; + if (std::isnan(year)) { + r = qSNaN(); + } else { + if ((Value::toInteger(year) >= 0) && (Value::toInteger(year) <= 99)) + year += 1900; + r = MakeDay(year, MonthFromTime(t), DateFromTime(t)); + r = UTC(MakeDate(r, TimeWithinDay(t))); + r = TimeClip(r); + } + self->value.setDouble(r); + return self->value; +} + +Value DatePrototype::method_setUTCFullYear(SimpleCallContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = self->value.asDouble(); + double year = ctx->argument(0).toNumber(); + double month = (ctx->argumentCount < 2) ? MonthFromTime(t) : ctx->argument(1).toNumber(); + double date = (ctx->argumentCount < 3) ? DateFromTime(t) : ctx->argument(2).toNumber(); + t = TimeClip(UTC(MakeDate(MakeDay(year, month, date), TimeWithinDay(t)))); + self->value.setDouble(t); + return self->value; +} + +Value DatePrototype::method_setFullYear(SimpleCallContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = LocalTime(self->value.asDouble()); + if (std::isnan(t)) + t = 0; + double year = ctx->argument(0).toNumber(); + double month = (ctx->argumentCount < 2) ? MonthFromTime(t) : ctx->argument(1).toNumber(); + double date = (ctx->argumentCount < 3) ? DateFromTime(t) : ctx->argument(2).toNumber(); + t = TimeClip(UTC(MakeDate(MakeDay(year, month, date), TimeWithinDay(t)))); + self->value.setDouble(t); + return self->value; +} + +Value DatePrototype::method_toUTCString(SimpleCallContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = self->value.asDouble(); + return Value::fromString(ctx, ToUTCString(t)); +} + +static void addZeroPrefixedInt(QString &str, int num, int nDigits) +{ + str.resize(str.size() + nDigits); + + QChar *c = str.data() + str.size() - 1; + while (nDigits) { + *c = QChar(num % 10 + '0'); + num /= 10; + --c; + --nDigits; + } +} + +Value DatePrototype::method_toISOString(SimpleCallContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = self->value.asDouble(); + if (!std::isfinite(t)) + ctx->throwRangeError(ctx->thisObject); + + QString result; + int year = (int)YearFromTime(t); + if (year < 0 || year > 9999) { + if (qAbs(year) >= 1000000) + return Value::fromString(ctx, QStringLiteral("Invalid Date")); + result += year < 0 ? '-' : '+'; + year = qAbs(year); + addZeroPrefixedInt(result, year, 6); + } else { + addZeroPrefixedInt(result, year, 4); + } + result += '-'; + addZeroPrefixedInt(result, (int)MonthFromTime(t) + 1, 2); + result += '-'; + addZeroPrefixedInt(result, (int)DateFromTime(t), 2); + result += 'T'; + addZeroPrefixedInt(result, HourFromTime(t), 2); + result += ':'; + addZeroPrefixedInt(result, MinFromTime(t), 2); + result += ':'; + addZeroPrefixedInt(result, SecFromTime(t), 2); + result += '.'; + addZeroPrefixedInt(result, msFromTime(t), 3); + result += 'Z'; + + return Value::fromString(ctx, result); +} + +Value DatePrototype::method_toJSON(SimpleCallContext *ctx) +{ + Value O = __qmljs_to_object(ctx, ctx->thisObject); + Value tv = __qmljs_to_primitive(O, NUMBER_HINT); + + if (tv.isNumber() && !std::isfinite(tv.toNumber())) + return Value::nullValue(); + + FunctionObject *toIso = O.objectValue()->get(ctx->engine->newString(QStringLiteral("toISOString"))).asFunctionObject(); + + if (!toIso) + ctx->throwTypeError(); + + return toIso->call(ctx->thisObject, 0, 0); +} + +void DatePrototype::timezoneUpdated() +{ + LocalTZA = getLocalTZA(); +} diff --git a/src/qml/jsruntime/qv4dateobject_p.h b/src/qml/jsruntime/qv4dateobject_p.h new file mode 100644 index 0000000000..4e833e143f --- /dev/null +++ b/src/qml/jsruntime/qv4dateobject_p.h @@ -0,0 +1,137 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4DATEOBJECT_P_H +#define QV4DATEOBJECT_P_H + +#include "qv4object_p.h" +#include "qv4functionobject_p.h" +#include + +QT_BEGIN_NAMESPACE + +class QDateTime; + +namespace QV4 { + +struct DateObject: Object { + Value value; + DateObject(ExecutionEngine *engine, const Value &value): Object(engine), value(value) { type = Type_DateObject; } + DateObject(ExecutionEngine *engine, const QDateTime &value); + + QDateTime toQDateTime() const; +}; + +struct DateCtor: FunctionObject +{ + DateCtor(ExecutionContext *scope); + + static Value construct(Managed *, Value *args, int argc); + static Value call(Managed *that, const Value &, Value *, int); + +protected: + static const ManagedVTable static_vtbl; +}; + +struct DatePrototype: DateObject +{ + DatePrototype(ExecutionEngine *engine): DateObject(engine, Value::fromDouble(qSNaN())) {} + void init(ExecutionContext *ctx, const Value &ctor); + + static double getThisDate(ExecutionContext *ctx); + + static Value method_parse(SimpleCallContext *ctx); + static Value method_UTC(SimpleCallContext *ctx); + static Value method_now(SimpleCallContext *ctx); + + static Value method_toString(SimpleCallContext *ctx); + static Value method_toDateString(SimpleCallContext *ctx); + static Value method_toTimeString(SimpleCallContext *ctx); + static Value method_toLocaleString(SimpleCallContext *ctx); + static Value method_toLocaleDateString(SimpleCallContext *ctx); + static Value method_toLocaleTimeString(SimpleCallContext *ctx); + static Value method_valueOf(SimpleCallContext *ctx); + static Value method_getTime(SimpleCallContext *ctx); + static Value method_getYear(SimpleCallContext *ctx); + static Value method_getFullYear(SimpleCallContext *ctx); + static Value method_getUTCFullYear(SimpleCallContext *ctx); + static Value method_getMonth(SimpleCallContext *ctx); + static Value method_getUTCMonth(SimpleCallContext *ctx); + static Value method_getDate(SimpleCallContext *ctx); + static Value method_getUTCDate(SimpleCallContext *ctx); + static Value method_getDay(SimpleCallContext *ctx); + static Value method_getUTCDay(SimpleCallContext *ctx); + static Value method_getHours(SimpleCallContext *ctx); + static Value method_getUTCHours(SimpleCallContext *ctx); + static Value method_getMinutes(SimpleCallContext *ctx); + static Value method_getUTCMinutes(SimpleCallContext *ctx); + static Value method_getSeconds(SimpleCallContext *ctx); + static Value method_getUTCSeconds(SimpleCallContext *ctx); + static Value method_getMilliseconds(SimpleCallContext *ctx); + static Value method_getUTCMilliseconds(SimpleCallContext *ctx); + static Value method_getTimezoneOffset(SimpleCallContext *ctx); + static Value method_setTime(SimpleCallContext *ctx); + static Value method_setMilliseconds(SimpleCallContext *ctx); + static Value method_setUTCMilliseconds(SimpleCallContext *ctx); + static Value method_setSeconds(SimpleCallContext *ctx); + static Value method_setUTCSeconds(SimpleCallContext *ctx); + static Value method_setMinutes(SimpleCallContext *ctx); + static Value method_setUTCMinutes(SimpleCallContext *ctx); + static Value method_setHours(SimpleCallContext *ctx); + static Value method_setUTCHours(SimpleCallContext *ctx); + static Value method_setDate(SimpleCallContext *ctx); + static Value method_setUTCDate(SimpleCallContext *ctx); + static Value method_setMonth(SimpleCallContext *ctx); + static Value method_setUTCMonth(SimpleCallContext *ctx); + static Value method_setYear(SimpleCallContext *ctx); + static Value method_setFullYear(SimpleCallContext *ctx); + static Value method_setUTCFullYear(SimpleCallContext *ctx); + static Value method_toUTCString(SimpleCallContext *ctx); + static Value method_toISOString(SimpleCallContext *ctx); + static Value method_toJSON(SimpleCallContext *ctx); + + static void timezoneUpdated(); +}; + +} + +QT_END_NAMESPACE + +#endif // QV4ECMAOBJECTS_P_H diff --git a/src/qml/jsruntime/qv4debugging.cpp b/src/qml/jsruntime/qv4debugging.cpp new file mode 100644 index 0000000000..1b182eac89 --- /dev/null +++ b/src/qml/jsruntime/qv4debugging.cpp @@ -0,0 +1,372 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4debugging_p.h" +#include "qv4object_p.h" +#include "qv4functionobject_p.h" +#include "qv4function_p.h" +#include "qv4instr_moth_p.h" +#include + +using namespace QV4; +using namespace QV4::Debugging; + +Debugger::Debugger(QV4::ExecutionEngine *engine) + : _engine(engine) + , m_agent(0) + , m_state(Running) + , m_pauseRequested(false) + , m_currentInstructionPointer(0) +{ + qMetaTypeId(); +} + +Debugger::~Debugger() +{ + detachFromAgent(); +} + +void Debugger::attachToAgent(DebuggerAgent *agent) +{ + Q_ASSERT(!m_agent); + m_agent = agent; +} + +void Debugger::detachFromAgent() +{ + DebuggerAgent *agent = 0; + { + QMutexLocker locker(&m_lock); + agent = m_agent; + m_agent = 0; + } + if (agent) + agent->removeDebugger(this); +} + +void Debugger::pause() +{ + QMutexLocker locker(&m_lock); + if (m_state == Paused) + return; + m_pauseRequested = true; +} + +void Debugger::resume() +{ + QMutexLocker locker(&m_lock); + Q_ASSERT(m_state == Paused); + m_runningCondition.wakeAll(); +} + +void Debugger::addBreakPoint(const QString &fileName, int lineNumber) +{ + QMutexLocker locker(&m_lock); + if (!m_pendingBreakPointsToRemove.remove(fileName, lineNumber)) + m_pendingBreakPointsToAdd.add(fileName, lineNumber); + m_havePendingBreakPoints = !m_pendingBreakPointsToAdd.isEmpty() || !m_pendingBreakPointsToRemove.isEmpty(); +} + +void Debugger::removeBreakPoint(const QString &fileName, int lineNumber) +{ + QMutexLocker locker(&m_lock); + if (!m_pendingBreakPointsToAdd.remove(fileName, lineNumber)) + m_pendingBreakPointsToRemove.add(fileName, lineNumber); + m_havePendingBreakPoints = !m_pendingBreakPointsToAdd.isEmpty() || !m_pendingBreakPointsToRemove.isEmpty(); +} + +Debugger::ExecutionState Debugger::currentExecutionState(const uchar *code) const +{ + if (!code) + code = m_currentInstructionPointer; + // ### Locking + ExecutionState state; + + QV4::ExecutionContext *context = _engine->current; + QV4::Function *function = 0; + if (CallContext *callCtx = context->asCallContext()) + function = callCtx->function->function; + else { + Q_ASSERT(context->type == QV4::ExecutionContext::Type_GlobalContext); + function = context->engine->globalCode; + } + + state.function = function; + state.fileName = function->sourceFile; + + qptrdiff relativeProgramCounter = code - function->codeData; + state.lineNumber = function->lineNumberForProgramCounter(relativeProgramCounter); + + return state; +} + +void Debugger::setPendingBreakpoints(Function *function) +{ + m_pendingBreakPointsToAddToFutureCode.applyToFunction(function, /*removeBreakPoints*/ false); +} + +void Debugger::maybeBreakAtInstruction(const uchar *code, bool breakPointHit) +{ + QMutexLocker locker(&m_lock); + m_currentInstructionPointer = code; + + // Do debugger internal work + if (m_havePendingBreakPoints) { + + if (breakPointHit) { + ExecutionState state = currentExecutionState(); + breakPointHit = !m_pendingBreakPointsToRemove.contains(state.fileName, state.lineNumber); + } + + applyPendingBreakPoints(); + } + + // Serve debugging requests from the agent + if (m_pauseRequested) { + m_pauseRequested = false; + pauseAndWait(); + } else if (breakPointHit) + pauseAndWait(); + + if (!m_pendingBreakPointsToAdd.isEmpty() || !m_pendingBreakPointsToRemove.isEmpty()) + applyPendingBreakPoints(); +} + +void Debugger::aboutToThrow(const QV4::Value &value) +{ + qDebug() << "*** We are about to throw..."; +} + +void Debugger::pauseAndWait() +{ + m_state = Paused; + QMetaObject::invokeMethod(m_agent, "debuggerPaused", Qt::QueuedConnection, Q_ARG(QV4::Debugging::Debugger*, this)); + m_runningCondition.wait(&m_lock); + m_state = Running; +} + +void Debugger::applyPendingBreakPoints() +{ + foreach (Function *function, _engine->functions) { + m_pendingBreakPointsToAdd.applyToFunction(function, /*removeBreakPoints*/false); + m_pendingBreakPointsToRemove.applyToFunction(function, /*removeBreakPoints*/true); + } + + for (BreakPoints::ConstIterator it = m_pendingBreakPointsToAdd.constBegin(), + end = m_pendingBreakPointsToAdd.constEnd(); it != end; ++it) { + foreach (int lineNumber, it.value()) + m_pendingBreakPointsToAddToFutureCode.add(it.key(), lineNumber); + } + + m_pendingBreakPointsToAdd.clear(); + m_pendingBreakPointsToRemove.clear(); + m_havePendingBreakPoints = false; +} + +static void realDumpValue(QV4::Value v, QV4::ExecutionContext *ctx, std::string prefix) +{ + using namespace QV4; + using namespace std; + cout << prefix << "tag: " << hex << v.tag << dec << endl << prefix << "\t-> "; + switch (v.type()) { + case Value::Undefined_Type: cout << "Undefined" << endl; return; + case Value::Null_Type: cout << "Null" << endl; return; + case Value::Boolean_Type: cout << "Boolean"; break; + case Value::Integer_Type: cout << "Integer"; break; + case Value::Object_Type: cout << "Object"; break; + case Value::String_Type: cout << "String"; break; + default: cout << "UNKNOWN" << endl; return; + } + cout << endl; + + if (v.isBoolean()) { + cout << prefix << "\t-> " << (v.booleanValue() ? "TRUE" : "FALSE") << endl; + return; + } + + if (v.isInteger()) { + cout << prefix << "\t-> " << v.integerValue() << endl; + return; + } + + if (v.isDouble()) { + cout << prefix << "\t-> " << v.doubleValue() << endl; + return; + } + + if (v.isString()) { + // maybe check something on the Managed object? + cout << prefix << "\t-> @" << hex << v.stringValue() << endl; + cout << prefix << "\t-> \"" << qPrintable(v.stringValue()->toQString()) << "\"" << endl; + return; + } + + Object *o = v.objectValue(); + if (!o) + return; + + cout << prefix << "\t-> @" << hex << o << endl; + cout << prefix << "object type: " << o->internalType() << endl << prefix << "\t-> "; + switch (o->internalType()) { + case QV4::Managed::Type_Invalid: cout << "Invalid"; break; + case QV4::Managed::Type_String: cout << "String"; break; + case QV4::Managed::Type_Object: cout << "Object"; break; + case QV4::Managed::Type_ArrayObject: cout << "ArrayObject"; break; + case QV4::Managed::Type_FunctionObject: cout << "FunctionObject"; break; + case QV4::Managed::Type_BooleanObject: cout << "BooleanObject"; break; + case QV4::Managed::Type_NumberObject: cout << "NumberObject"; break; + case QV4::Managed::Type_StringObject: cout << "StringObject"; break; + case QV4::Managed::Type_DateObject: cout << "DateObject"; break; + case QV4::Managed::Type_RegExpObject: cout << "RegExpObject"; break; + case QV4::Managed::Type_ErrorObject: cout << "ErrorObject"; break; + case QV4::Managed::Type_ArgumentsObject: cout << "ArgumentsObject"; break; + case QV4::Managed::Type_JSONObject: cout << "JSONObject"; break; + case QV4::Managed::Type_MathObject: cout << "MathObject"; break; + case QV4::Managed::Type_ForeachIteratorObject: cout << "ForeachIteratorObject"; break; + default: cout << "UNKNOWN" << endl; return; + } + cout << endl; + + cout << prefix << "properties:" << endl; + ForEachIteratorObject it(ctx, o); + for (Value name = it.nextPropertyName(); !name.isNull(); name = it.nextPropertyName()) { + cout << prefix << "\t\"" << qPrintable(name.stringValue()->toQString()) << "\"" << endl; + PropertyAttributes attrs; + Property *d = o->__getOwnProperty__(name.stringValue(), &attrs); + Value pval = o->getValue(d, attrs); + cout << prefix << "\tvalue:" << endl; + realDumpValue(pval, ctx, prefix + "\t"); + } +} + +void dumpValue(QV4::Value v, QV4::ExecutionContext *ctx) +{ + realDumpValue(v, ctx, std::string("")); +} + + +void DebuggerAgent::addDebugger(Debugger *debugger) +{ + Q_ASSERT(!m_debuggers.contains(debugger)); + m_debuggers << debugger; + debugger->attachToAgent(this); +} + +void DebuggerAgent::removeDebugger(Debugger *debugger) +{ + m_debuggers.removeAll(debugger); + debugger->detachFromAgent(); +} + +void DebuggerAgent::pause(Debugger *debugger) +{ + debugger->pause(); +} + +void DebuggerAgent::addBreakPoint(Debugger *debugger, const QString &fileName, int lineNumber) +{ + debugger->addBreakPoint(fileName, lineNumber); +} + +void DebuggerAgent::removeBreakPoint(Debugger *debugger, const QString &fileName, int lineNumber) +{ + debugger->removeBreakPoint(fileName, lineNumber); +} + +DebuggerAgent::~DebuggerAgent() +{ + Q_ASSERT(m_debuggers.isEmpty()); +} + +void Debugger::BreakPoints::add(const QString &fileName, int lineNumber) +{ + QList &lines = (*this)[fileName]; + if (!lines.contains(lineNumber)) { + lines.append(lineNumber); + qSort(lines); + } +} + +bool Debugger::BreakPoints::remove(const QString &fileName, int lineNumber) +{ + Iterator breakPoints = find(fileName); + if (breakPoints == constEnd()) + return false; + return breakPoints->removeAll(lineNumber) > 0; +} + +bool Debugger::BreakPoints::contains(const QString &fileName, int lineNumber) const +{ + ConstIterator breakPoints = find(fileName); + if (breakPoints == constEnd()) + return false; + return breakPoints->contains(lineNumber); +} + +void Debugger::BreakPoints::applyToFunction(Function *function, bool removeBreakPoints) +{ + Iterator breakPointsForFile = find(function->sourceFile); + if (breakPointsForFile == end()) + return; + + QList::Iterator breakPoint = breakPointsForFile->begin(); + while (breakPoint != breakPointsForFile->end()) { + bool breakPointFound = false; + for (QVector::ConstIterator mapping = function->lineNumberMappings.constBegin(), + end = function->lineNumberMappings.constEnd(); mapping != end; ++mapping) { + if (mapping->lineNumber == *breakPoint) { + uchar *codePtr = const_cast(function->codeData) + mapping->codeOffset; + QQmlJS::Moth::Instr *instruction = reinterpret_cast(codePtr); + instruction->common.breakPoint = !removeBreakPoints; + // Continue setting the next break point. + breakPointFound = true; + break; + } + } + if (breakPointFound) + breakPoint = breakPointsForFile->erase(breakPoint); + else + ++breakPoint; + } + + if (breakPointsForFile->isEmpty()) + erase(breakPointsForFile); +} diff --git a/src/qml/jsruntime/qv4debugging_p.h b/src/qml/jsruntime/qv4debugging_p.h new file mode 100644 index 0000000000..d71b25f378 --- /dev/null +++ b/src/qml/jsruntime/qv4debugging_p.h @@ -0,0 +1,160 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DEBUGGING_H +#define DEBUGGING_H + +#include "qv4global_p.h" +#include "qv4engine_p.h" +#include "qv4context_p.h" + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct Function; + +namespace Debugging { + +class DebuggerAgent; + +class Q_QML_EXPORT Debugger +{ +public: + enum State { + Running, + Paused + }; + + Debugger(ExecutionEngine *_engine); + ~Debugger(); + + void attachToAgent(DebuggerAgent *agent); + void detachFromAgent(); + + void pause(); + void resume(); + + State state() const { return m_state; } + + void addBreakPoint(const QString &fileName, int lineNumber); + void removeBreakPoint(const QString &fileName, int lineNumber); + + struct ExecutionState + { + ExecutionState() : lineNumber(-1), function(0) {} + QString fileName; + int lineNumber; + Function *function; + }; + + ExecutionState currentExecutionState(const uchar *code = 0) const; + + bool pauseAtNextOpportunity() const { + return m_pauseRequested || m_havePendingBreakPoints; + } + void setPendingBreakpoints(Function *function); + +public: // compile-time interface + void maybeBreakAtInstruction(const uchar *code, bool breakPointHit); + +public: // execution hooks + void aboutToThrow(const Value &value); + +private: + // requires lock to be held + void pauseAndWait(); + + void applyPendingBreakPoints(); + + struct BreakPoints : public QHash > + { + void add(const QString &fileName, int lineNumber); + bool remove(const QString &fileName, int lineNumber); + bool contains(const QString &fileName, int lineNumber) const; + void applyToFunction(Function *function, bool removeBreakPoints); + }; + + QV4::ExecutionEngine *_engine; + DebuggerAgent *m_agent; + QMutex m_lock; + QWaitCondition m_runningCondition; + State m_state; + bool m_pauseRequested; + bool m_havePendingBreakPoints; + BreakPoints m_pendingBreakPointsToAdd; + BreakPoints m_pendingBreakPointsToAddToFutureCode; + BreakPoints m_pendingBreakPointsToRemove; + const uchar *m_currentInstructionPointer; +}; + +class Q_QML_EXPORT DebuggerAgent : public QObject +{ + Q_OBJECT +public: + ~DebuggerAgent(); + + void addDebugger(Debugger *debugger); + void removeDebugger(Debugger *debugger); + + void pause(Debugger *debugger); + void addBreakPoint(Debugger *debugger, const QString &fileName, int lineNumber); + void removeBreakPoint(Debugger *debugger, const QString &fileName, int lineNumber); + + Q_INVOKABLE virtual void debuggerPaused(QV4::Debugging::Debugger *debugger) = 0; + +protected: + QList m_debuggers; +}; + +} // namespace Debugging +} // namespace QV4 + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QV4::Debugging::Debugger*) + +#endif // DEBUGGING_H diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp new file mode 100644 index 0000000000..0b7c850aa6 --- /dev/null +++ b/src/qml/jsruntime/qv4engine.cpp @@ -0,0 +1,837 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "qv4function_p.h" +#include +#include +#include +#include +#include +#include "qv4mm_p.h" +#include +#include +#include +#include +#include +#include +#include "qv4debugging_p.h" +#include "qv4executableallocator_p.h" +#include "qv4sequenceobject_p.h" +#include "qv4qobjectwrapper_p.h" +#include "qv4qmlextensions_p.h" +#include "qv4stacktrace_p.h" + +#ifdef V4_ENABLE_JIT +#include "qv4isel_masm_p.h" +#endif // V4_ENABLE_JIT + +#include "qv4isel_moth_p.h" + +QT_BEGIN_NAMESPACE + +using namespace QV4; + +static QBasicAtomicInt engineSerial = Q_BASIC_ATOMIC_INITIALIZER(1); + +ExecutionEngine::ExecutionEngine(QQmlJS::EvalISelFactory *factory) + : memoryManager(new QV4::MemoryManager) + , executableAllocator(new QV4::ExecutableAllocator) + , regExpAllocator(new QV4::ExecutableAllocator) + , bumperPointerAllocator(new WTF::BumpPointerAllocator) + , debugger(0) + , globalObject(0) + , globalCode(0) + , functionsNeedSort(false) + , m_engineId(engineSerial.fetchAndAddOrdered(1)) + , regExpCache(0) + , m_multiplyWrappedQObjects(0) + , m_qmlExtensions(0) +{ + MemoryManager::GCBlocker gcBlocker(memoryManager); + + if (!factory) { +#ifdef V4_ENABLE_JIT + factory = new QQmlJS::MASM::ISelFactory; +#else // !V4_ENABLE_JIT + factory = new QQmlJS::Moth::ISelFactory; +#endif // V4_ENABLE_JIT + } + iselFactory.reset(factory); + + memoryManager->setExecutionEngine(this); + + identifierTable = new IdentifierTable(this); + + emptyClass = new (classPool.allocate(sizeof(InternalClass))) InternalClass(this); + + id_undefined = newIdentifier(QStringLiteral("undefined")); + id_null = newIdentifier(QStringLiteral("null")); + id_true = newIdentifier(QStringLiteral("true")); + id_false = newIdentifier(QStringLiteral("false")); + id_boolean = newIdentifier(QStringLiteral("boolean")); + id_number = newIdentifier(QStringLiteral("number")); + id_string = newIdentifier(QStringLiteral("string")); + id_object = newIdentifier(QStringLiteral("object")); + id_function = newIdentifier(QStringLiteral("function")); + id_length = newIdentifier(QStringLiteral("length")); + id_prototype = newIdentifier(QStringLiteral("prototype")); + id_constructor = newIdentifier(QStringLiteral("constructor")); + id_arguments = newIdentifier(QStringLiteral("arguments")); + id_caller = newIdentifier(QStringLiteral("caller")); + id_this = newIdentifier(QStringLiteral("this")); + id___proto__ = newIdentifier(QStringLiteral("__proto__")); + id_enumerable = newIdentifier(QStringLiteral("enumerable")); + id_configurable = newIdentifier(QStringLiteral("configurable")); + id_writable = newIdentifier(QStringLiteral("writable")); + id_value = newIdentifier(QStringLiteral("value")); + id_get = newIdentifier(QStringLiteral("get")); + id_set = newIdentifier(QStringLiteral("set")); + id_eval = newIdentifier(QStringLiteral("eval")); + id_uintMax = newIdentifier(QStringLiteral("4294967295")); + id_name = newIdentifier(QStringLiteral("name")); + + arrayClass = emptyClass->addMember(id_length, Attr_NotConfigurable|Attr_NotEnumerable); + initRootContext(); + + objectPrototype = new (memoryManager) ObjectPrototype(this); + stringPrototype = new (memoryManager) StringPrototype(this); + numberPrototype = new (memoryManager) NumberPrototype(this); + booleanPrototype = new (memoryManager) BooleanPrototype(this); + arrayPrototype = new (memoryManager) ArrayPrototype(rootContext); + datePrototype = new (memoryManager) DatePrototype(this); + functionPrototype = new (memoryManager) FunctionPrototype(rootContext); + regExpPrototype = new (memoryManager) RegExpPrototype(this); + errorPrototype = new (memoryManager) ErrorPrototype(this); + evalErrorPrototype = new (memoryManager) EvalErrorPrototype(this); + rangeErrorPrototype = new (memoryManager) RangeErrorPrototype(this); + referenceErrorPrototype = new (memoryManager) ReferenceErrorPrototype(this); + syntaxErrorPrototype = new (memoryManager) SyntaxErrorPrototype(this); + typeErrorPrototype = new (memoryManager) TypeErrorPrototype(this); + uRIErrorPrototype = new (memoryManager) URIErrorPrototype(this); + + variantPrototype = new (memoryManager) VariantPrototype(this); + sequencePrototype = new (memoryManager) SequencePrototype(this); + + stringPrototype->prototype = objectPrototype; + numberPrototype->prototype = objectPrototype; + booleanPrototype->prototype = objectPrototype; + arrayPrototype->prototype = objectPrototype; + datePrototype->prototype = objectPrototype; + functionPrototype->prototype = objectPrototype; + regExpPrototype->prototype = objectPrototype; + errorPrototype->prototype = objectPrototype; + evalErrorPrototype->prototype = objectPrototype; + rangeErrorPrototype->prototype = objectPrototype; + referenceErrorPrototype->prototype = objectPrototype; + syntaxErrorPrototype->prototype = objectPrototype; + typeErrorPrototype->prototype = objectPrototype; + uRIErrorPrototype->prototype = objectPrototype; + + objectCtor = Value::fromObject(new (memoryManager) ObjectCtor(rootContext)); + stringCtor = Value::fromObject(new (memoryManager) StringCtor(rootContext)); + numberCtor = Value::fromObject(new (memoryManager) NumberCtor(rootContext)); + booleanCtor = Value::fromObject(new (memoryManager) BooleanCtor(rootContext)); + arrayCtor = Value::fromObject(new (memoryManager) ArrayCtor(rootContext)); + functionCtor = Value::fromObject(new (memoryManager) FunctionCtor(rootContext)); + dateCtor = Value::fromObject(new (memoryManager) DateCtor(rootContext)); + regExpCtor = Value::fromObject(new (memoryManager) RegExpCtor(rootContext)); + errorCtor = Value::fromObject(new (memoryManager) ErrorCtor(rootContext)); + evalErrorCtor = Value::fromObject(new (memoryManager) EvalErrorCtor(rootContext)); + rangeErrorCtor = Value::fromObject(new (memoryManager) RangeErrorCtor(rootContext)); + referenceErrorCtor = Value::fromObject(new (memoryManager) ReferenceErrorCtor(rootContext)); + syntaxErrorCtor = Value::fromObject(new (memoryManager) SyntaxErrorCtor(rootContext)); + typeErrorCtor = Value::fromObject(new (memoryManager) TypeErrorCtor(rootContext)); + uRIErrorCtor = Value::fromObject(new (memoryManager) URIErrorCtor(rootContext)); + + objectCtor.objectValue()->prototype = functionPrototype; + stringCtor.objectValue()->prototype = functionPrototype; + numberCtor.objectValue()->prototype = functionPrototype; + booleanCtor.objectValue()->prototype = functionPrototype; + arrayCtor.objectValue()->prototype = functionPrototype; + functionCtor.objectValue()->prototype = functionPrototype; + dateCtor.objectValue()->prototype = functionPrototype; + regExpCtor.objectValue()->prototype = functionPrototype; + errorCtor.objectValue()->prototype = functionPrototype; + evalErrorCtor.objectValue()->prototype = functionPrototype; + rangeErrorCtor.objectValue()->prototype = functionPrototype; + referenceErrorCtor.objectValue()->prototype = functionPrototype; + syntaxErrorCtor.objectValue()->prototype = functionPrototype; + typeErrorCtor.objectValue()->prototype = functionPrototype; + uRIErrorCtor.objectValue()->prototype = functionPrototype; + + objectPrototype->init(rootContext, objectCtor); + stringPrototype->init(this, stringCtor); + numberPrototype->init(rootContext, numberCtor); + booleanPrototype->init(rootContext, booleanCtor); + arrayPrototype->init(rootContext, arrayCtor); + datePrototype->init(rootContext, dateCtor); + functionPrototype->init(rootContext, functionCtor); + regExpPrototype->init(rootContext, regExpCtor); + errorPrototype->init(this, errorCtor); + evalErrorPrototype->init(this, evalErrorCtor); + rangeErrorPrototype->init(this, rangeErrorCtor); + referenceErrorPrototype->init(this, referenceErrorCtor); + syntaxErrorPrototype->init(this, syntaxErrorCtor); + typeErrorPrototype->init(this, typeErrorCtor); + uRIErrorPrototype->init(this, uRIErrorCtor); + + variantPrototype->init(this); + sequencePrototype->init(this); + + // + // set up the global object + // + globalObject = newObject(/*rootContext*/); + rootContext->global = globalObject; + rootContext->thisObject = Value::fromObject(globalObject); + + globalObject->defineDefaultProperty(rootContext, QStringLiteral("Object"), objectCtor); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("String"), stringCtor); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("Number"), numberCtor); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("Boolean"), booleanCtor); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("Array"), arrayCtor); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("Function"), functionCtor); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("Date"), dateCtor); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("RegExp"), regExpCtor); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("Error"), errorCtor); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("EvalError"), evalErrorCtor); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("RangeError"), rangeErrorCtor); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("ReferenceError"), referenceErrorCtor); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("SyntaxError"), syntaxErrorCtor); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("TypeError"), typeErrorCtor); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("URIError"), uRIErrorCtor); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("Math"), Value::fromObject(new (memoryManager) MathObject(rootContext))); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("JSON"), Value::fromObject(new (memoryManager) JsonObject(rootContext))); + + globalObject->defineReadonlyProperty(this, QStringLiteral("undefined"), Value::undefinedValue()); + globalObject->defineReadonlyProperty(this, QStringLiteral("NaN"), Value::fromDouble(std::numeric_limits::quiet_NaN())); + globalObject->defineReadonlyProperty(this, QStringLiteral("Infinity"), Value::fromDouble(Q_INFINITY)); + + evalFunction = new (memoryManager) EvalFunction(rootContext); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("eval"), Value::fromObject(evalFunction)); + + globalObject->defineDefaultProperty(rootContext, QStringLiteral("parseInt"), GlobalFunctions::method_parseInt, 2); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("parseFloat"), GlobalFunctions::method_parseFloat, 1); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("isNaN"), GlobalFunctions::method_isNaN, 1); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("isFinite"), GlobalFunctions::method_isFinite, 1); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("decodeURI"), GlobalFunctions::method_decodeURI, 1); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("decodeURIComponent"), GlobalFunctions::method_decodeURIComponent, 1); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("encodeURI"), GlobalFunctions::method_encodeURI, 1); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("encodeURIComponent"), GlobalFunctions::method_encodeURIComponent, 1); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("escape"), GlobalFunctions::method_escape, 1); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("unescape"), GlobalFunctions::method_unescape, 1); +} + +ExecutionEngine::~ExecutionEngine() +{ + delete debugger; + delete m_multiplyWrappedQObjects; + m_multiplyWrappedQObjects = 0; + delete memoryManager; + delete m_qmlExtensions; + emptyClass->destroy(); + delete identifierTable; + delete bumperPointerAllocator; + delete regExpCache; + UnwindHelper::deregisterFunctions(functions); + qDeleteAll(functions); + delete regExpAllocator; + delete executableAllocator; +} + +void ExecutionEngine::enableDebugger() +{ + Q_ASSERT(!debugger); + debugger = new Debugging::Debugger(this); + iselFactory.reset(new QQmlJS::Moth::ISelFactory); +} + +void ExecutionEngine::initRootContext() +{ + rootContext = static_cast(memoryManager->allocContext(sizeof(GlobalContext))); + current = rootContext; + current->parent = 0; + rootContext->initGlobalContext(this); +} + +InternalClass *ExecutionEngine::newClass(const InternalClass &other) +{ + return new (classPool.allocate(sizeof(InternalClass))) InternalClass(other); +} + +WithContext *ExecutionEngine::newWithContext(Object *with) +{ + WithContext *w = static_cast(memoryManager->allocContext(sizeof(WithContext))); + ExecutionContext *p = current; + current = w; + w->initWithContext(p, with); + return w; +} + +CatchContext *ExecutionEngine::newCatchContext(String *exceptionVarName, const Value &exceptionValue) +{ + CatchContext *c = static_cast(memoryManager->allocContext(sizeof(CatchContext))); + ExecutionContext *p = current; + current = c; + c->initCatchContext(p, exceptionVarName, exceptionValue); + return c; +} + +CallContext *ExecutionEngine::newCallContext(FunctionObject *f, const Value &thisObject, Value *args, int argc) +{ + CallContext *c = static_cast(memoryManager->allocContext(requiredMemoryForExecutionContect(f, argc))); + ExecutionContext *p = current; + current = c; + c->initCallContext(p, f, args, argc, thisObject); + + return c; +} + +CallContext *ExecutionEngine::newQmlContext(FunctionObject *f, Object *qml) +{ + CallContext *c = static_cast(memoryManager->allocContext(requiredMemoryForExecutionContect(f, 0))); + + ExecutionContext *p = current; + current = c; + c->initQmlContext(p, qml, f); + + return c; +} + +CallContext *ExecutionEngine::newCallContext(void *stackSpace, FunctionObject *f, const Value &thisObject, Value *args, int argc) +{ + CallContext *c; + uint memory = requiredMemoryForExecutionContect(f, argc); + if (f->needsActivation || memory > stackContextSize) { + c = static_cast(memoryManager->allocContext(memory)); + } else { + c = (CallContext *)stackSpace; +#ifndef QT_NO_DEBUG + c->next = (CallContext *)0x1; +#endif + } + + ExecutionContext *p = current; + current = c; + c->initCallContext(p, f, args, argc, thisObject); + + return c; +} + + +ExecutionContext *ExecutionEngine::pushGlobalContext() +{ + GlobalContext *g = static_cast(memoryManager->allocContext(sizeof(GlobalContext))); + ExecutionContext *oldNext = g->next; + *g = *rootContext; + g->next = oldNext; + g->parent = current; + current = g; + + return current; +} + +Function *ExecutionEngine::newFunction(const QString &name) +{ + Function *f = new Function(newIdentifier(name)); + functions.append(f); + functionsNeedSort = true; + return f; +} + +FunctionObject *ExecutionEngine::newBuiltinFunction(ExecutionContext *scope, String *name, Value (*code)(SimpleCallContext *)) +{ + BuiltinFunctionOld *f = new (memoryManager) BuiltinFunctionOld(scope, name, code); + return f; +} + +FunctionObject *ExecutionEngine::newScriptFunction(ExecutionContext *scope, Function *function) +{ + assert(function); + + ScriptFunction *f = new (memoryManager) ScriptFunction(scope, function); + return f; +} + +BoundFunction *ExecutionEngine::newBoundFunction(ExecutionContext *scope, FunctionObject *target, Value boundThis, const QVector &boundArgs) +{ + assert(target); + + BoundFunction *f = new (memoryManager) BoundFunction(scope, target, boundThis, boundArgs); + return f; +} + + +Object *ExecutionEngine::newObject() +{ + Object *object = new (memoryManager) Object(this); + object->prototype = objectPrototype; + return object; +} + +Object *ExecutionEngine::newObject(InternalClass *internalClass) +{ + Object *object = new (memoryManager) Object(this, internalClass); + object->prototype = objectPrototype; + return object; +} + +String *ExecutionEngine::newString(const QString &s) +{ + return new (memoryManager) String(this, s); +} + +String *ExecutionEngine::newIdentifier(const QString &text) +{ + return identifierTable->insertString(text); +} + +Object *ExecutionEngine::newStringObject(const Value &value) +{ + StringObject *object = new (memoryManager) StringObject(this, value); + object->prototype = stringPrototype; + return object; +} + +Object *ExecutionEngine::newNumberObject(const Value &value) +{ + NumberObject *object = new (memoryManager) NumberObject(this, value); + object->prototype = numberPrototype; + return object; +} + +Object *ExecutionEngine::newBooleanObject(const Value &value) +{ + Object *object = new (memoryManager) BooleanObject(this, value); + object->prototype = booleanPrototype; + return object; +} + +ArrayObject *ExecutionEngine::newArrayObject(int count) +{ + ArrayObject *object = new (memoryManager) ArrayObject(this); + object->prototype = arrayPrototype; + + if (count) { + if (count < 0x1000) + object->arrayReserve(count); + object->setArrayLengthUnchecked(count); + } + return object; +} + +ArrayObject *ExecutionEngine::newArrayObject(const QStringList &list) +{ + ArrayObject *object = new (memoryManager) ArrayObject(this, list); + object->prototype = arrayPrototype; + return object; +} + +DateObject *ExecutionEngine::newDateObject(const Value &value) +{ + DateObject *object = new (memoryManager) DateObject(this, value); + object->prototype = datePrototype; + return object; +} + +DateObject *ExecutionEngine::newDateObject(const QDateTime &dt) +{ + DateObject *object = new (memoryManager) DateObject(this, dt); + object->prototype = datePrototype; + return object; +} + +RegExpObject *ExecutionEngine::newRegExpObject(const QString &pattern, int flags) +{ + bool global = (flags & QQmlJS::V4IR::RegExp::RegExp_Global); + bool ignoreCase = false; + bool multiline = false; + if (flags & QQmlJS::V4IR::RegExp::RegExp_IgnoreCase) + ignoreCase = true; + if (flags & QQmlJS::V4IR::RegExp::RegExp_Multiline) + multiline = true; + + return newRegExpObject(RegExp::create(this, pattern, ignoreCase, multiline), global); +} + +RegExpObject *ExecutionEngine::newRegExpObject(RegExp* re, bool global) +{ + RegExpObject *object = new (memoryManager) RegExpObject(this, re, global); + object->prototype = regExpPrototype; + return object; +} + +RegExpObject *ExecutionEngine::newRegExpObject(const QRegExp &re) +{ + RegExpObject *object = new (memoryManager) RegExpObject(this, re); + object->prototype = regExpPrototype; + return object; +} + +Object *ExecutionEngine::newErrorObject(const Value &value) +{ + ErrorObject *object = new (memoryManager) ErrorObject(this, value); + object->prototype = errorPrototype; + return object; +} + +Object *ExecutionEngine::newSyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *message) +{ + return new (memoryManager) SyntaxErrorObject(ctx, message); +} + +Object *ExecutionEngine::newSyntaxErrorObject(const QString &message) +{ + return new (memoryManager) SyntaxErrorObject(this, message); +} + + +Object *ExecutionEngine::newReferenceErrorObject(const QString &message) +{ + return new (memoryManager) ReferenceErrorObject(this, message); +} + +Object *ExecutionEngine::newReferenceErrorObject(const QString &message, const QString &fileName, int lineNumber) +{ + return new (memoryManager) ReferenceErrorObject(this, message, fileName, lineNumber); +} + + +Object *ExecutionEngine::newTypeErrorObject(const QString &message) +{ + return new (memoryManager) TypeErrorObject(this, message); +} + +Object *ExecutionEngine::newRangeErrorObject(const QString &message) +{ + return new (memoryManager) RangeErrorObject(this, message); +} + +Object *ExecutionEngine::newURIErrorObject(Value message) +{ + return new (memoryManager) URIErrorObject(this, message); +} + +Object *ExecutionEngine::newVariantObject(const QVariant &v) +{ + return new (memoryManager) VariantObject(this, v); +} + +Object *ExecutionEngine::newForEachIteratorObject(ExecutionContext *ctx, Object *o) +{ + return new (memoryManager) ForEachIteratorObject(ctx, o); +} + +Object *ExecutionEngine::qmlContextObject() const +{ + ExecutionContext *ctx = current; + + if (ctx->type == QV4::ExecutionContext::Type_SimpleCallContext) + ctx = ctx->parent; + + if (!ctx->outer) + return 0; + + while (ctx->outer && ctx->outer->type != ExecutionContext::Type_GlobalContext) + ctx = ctx->outer; + + assert(ctx); + if (ctx->type != ExecutionContext::Type_QmlContext) + return 0; + + return static_cast(ctx)->activation; +} + +namespace { + struct LineNumberResolver { + const ExecutionEngine* engine; + QScopedPointer nativeTrace; + + LineNumberResolver(const ExecutionEngine *engine) + : engine(engine) + { + } + + void resolve(ExecutionEngine::StackFrame *frame, ExecutionContext *context, Function *function) + { + if (context->interpreterInstructionPointer) { + qptrdiff offset = *context->interpreterInstructionPointer - 1 - function->codeData; + frame->line = function->lineNumberForProgramCounter(offset); + } else { + if (!nativeTrace) + nativeTrace.reset(new QV4::NativeStackTrace(engine->current)); + + NativeFrame nativeFrame = nativeTrace->nextFrame(); + if (nativeFrame.function == function) + frame->line = nativeFrame.line; + } + } + }; +} + +QVector ExecutionEngine::stackTrace(int frameLimit) const +{ + LineNumberResolver lineNumbers(this); + + QVector stack; + + QV4::ExecutionContext *c = current; + while (c && frameLimit) { + if (CallContext *callCtx = c->asCallContext()) { + StackFrame frame; + if (callCtx->function->function) + frame.source = callCtx->function->function->sourceFile; + frame.function = callCtx->function->name->toQString(); + frame.line = -1; + frame.column = -1; + + if (callCtx->function->function) + lineNumbers.resolve(&frame, callCtx, callCtx->function->function); + + stack.append(frame); + --frameLimit; + } + c = c->parent; + } + + if (frameLimit && globalCode) { + StackFrame frame; + frame.source = globalCode->sourceFile; + frame.function = globalCode->name->toQString(); + frame.line = -1; + frame.column = -1; + + lineNumbers.resolve(&frame, rootContext, globalCode); + + stack.append(frame); + } + return stack; +} + +ExecutionEngine::StackFrame ExecutionEngine::currentStackFrame() const +{ + StackFrame frame; + frame.line = -1; + frame.column = -1; + + QVector trace = stackTrace(/*limit*/ 1); + if (!trace.isEmpty()) + frame = trace.first(); + + return frame; +} + +QUrl ExecutionEngine::resolvedUrl(const QString &file) +{ + QUrl src(file); + if (!src.isRelative()) + return src; + + QUrl base; + QV4::ExecutionContext *c = current; + while (c) { + if (CallContext *callCtx = c->asCallContext()) { + if (callCtx->function->function) + base.setUrl(callCtx->function->function->sourceFile); + break; + } + c = c->parent; + } + + if (base.isEmpty() && globalCode) + base.setUrl(globalCode->sourceFile); + + if (base.isEmpty()) + return src; + + return base.resolved(src); +} + +void ExecutionEngine::requireArgumentsAccessors(int n) +{ + if (n <= argumentsAccessors.size()) + return; + + uint oldSize = argumentsAccessors.size(); + argumentsAccessors.resize(n); + for (int i = oldSize; i < n; ++i) { + FunctionObject *get = new (memoryManager) ArgumentsGetterFunction(rootContext, i); + get->prototype = functionPrototype; + FunctionObject *set = new (memoryManager) ArgumentsSetterFunction(rootContext, i); + set->prototype = functionPrototype; + Property pd = Property::fromAccessor(get, set); + argumentsAccessors[i] = pd; + } +} + +void ExecutionEngine::markObjects() +{ + identifierTable->mark(); + + globalObject->mark(); + + if (globalCode) + globalCode->mark(); + + for (int i = 0; i < argumentsAccessors.size(); ++i) { + const Property &pd = argumentsAccessors.at(i); + if (FunctionObject *getter = pd.getter()) + getter->mark(); + if (FunctionObject *setter = pd.setter()) + setter->mark(); + } + + ExecutionContext *c = current; + while (c) { + c->mark(); + c = c->parent; + } + + for (int i = 0; i < functions.size(); ++i) + functions.at(i)->mark(); + + id_length->mark(); + id_prototype->mark(); + id_constructor->mark(); + id_arguments->mark(); + id_caller->mark(); + id_this->mark(); + id___proto__->mark(); + id_enumerable->mark(); + id_configurable->mark(); + id_writable->mark(); + id_value->mark(); + id_get->mark(); + id_set->mark(); + id_eval->mark(); + id_uintMax->mark(); + id_name->mark(); + + objectCtor.mark(); + stringCtor.mark(); + numberCtor.mark(); + booleanCtor.mark(); + arrayCtor.mark(); + functionCtor.mark(); + dateCtor.mark(); + regExpCtor.mark(); + errorCtor.mark(); + evalErrorCtor.mark(); + rangeErrorCtor.mark(); + referenceErrorCtor.mark(); + syntaxErrorCtor.mark(); + typeErrorCtor.mark(); + uRIErrorCtor.mark(); + + objectPrototype->mark(); + stringPrototype->mark(); + numberPrototype->mark(); + booleanPrototype->mark(); + arrayPrototype->mark(); + functionPrototype->mark(); + datePrototype->mark(); + regExpPrototype->mark(); + errorPrototype->mark(); + evalErrorPrototype->mark(); + rangeErrorPrototype->mark(); + referenceErrorPrototype->mark(); + syntaxErrorPrototype->mark(); + typeErrorPrototype->mark(); + uRIErrorPrototype->mark(); + + variantPrototype->mark(); + sequencePrototype->mark(); + + if (m_qmlExtensions) + m_qmlExtensions->markObjects(); +} + +namespace { + bool functionSortHelper(Function *lhs, Function *rhs) + { + return reinterpret_cast(lhs->code) < reinterpret_cast(rhs->code); + } + + struct FindHelper + { + bool operator()(Function *function, quintptr pc) + { + return reinterpret_cast(function->code) < pc + && (reinterpret_cast(function->code) + function->codeSize) < pc; + } + + bool operator()(quintptr pc, Function *function) + { + return pc < reinterpret_cast(function->code); + } + }; +} + +Function *ExecutionEngine::functionForProgramCounter(quintptr pc) const +{ + if (functionsNeedSort) { + qSort(functions.begin(), functions.end(), functionSortHelper); + functionsNeedSort = false; + } + + QVector::ConstIterator it = qBinaryFind(functions.constBegin(), functions.constEnd(), + pc, FindHelper()); + if (it != functions.constEnd()) + return *it; + return 0; +} + +QmlExtensions *ExecutionEngine::qmlExtensions() +{ + if (!m_qmlExtensions) + m_qmlExtensions = new QmlExtensions; + return m_qmlExtensions; +} + +QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h new file mode 100644 index 0000000000..a6bf2ef38e --- /dev/null +++ b/src/qml/jsruntime/qv4engine_p.h @@ -0,0 +1,332 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4ENGINE_H +#define QV4ENGINE_H + +#include "qv4global_p.h" +#include "private/qv4isel_p.h" +#include "qv4util_p.h" +#include "qv4context_p.h" +#include "qv4property_p.h" +#include + +namespace WTF { +class BumpPointerAllocator; +} + +QT_BEGIN_NAMESPACE + +class QV8Engine; + +namespace QV4 { +namespace Debugging { +class Debugger; +} // namespace Debugging +} + +namespace QV4 { + +struct Function; +struct Object; +struct BooleanObject; +struct NumberObject; +struct StringObject; +struct ArrayObject; +struct DateObject; +struct FunctionObject; +struct BoundFunction; +struct RegExpObject; +struct ErrorObject; +struct ArgumentsObject; +struct ExecutionContext; +struct ExecutionEngine; +class MemoryManager; +class UnwindHelper; +class ExecutableAllocator; + +struct ObjectPrototype; +struct StringPrototype; +struct NumberPrototype; +struct BooleanPrototype; +struct ArrayPrototype; +struct FunctionPrototype; +struct DatePrototype; +struct RegExpPrototype; +struct ErrorPrototype; +struct EvalErrorPrototype; +struct RangeErrorPrototype; +struct ReferenceErrorPrototype; +struct SyntaxErrorPrototype; +struct TypeErrorPrototype; +struct URIErrorPrototype; +struct VariantPrototype; +struct SequencePrototype; +struct EvalFunction; +struct IdentifierTable; +struct InternalClass; +class MultiplyWrappedQObjectMap; +class RegExp; +class RegExpCache; +struct QmlExtensions; + +struct Q_QML_EXPORT ExecutionEngine +{ + MemoryManager *memoryManager; + ExecutableAllocator *executableAllocator; + ExecutableAllocator *regExpAllocator; + QScopedPointer iselFactory; + + ExecutionContext *current; + GlobalContext *rootContext; + + WTF::BumpPointerAllocator *bumperPointerAllocator; // Used by Yarr Regex engine. + + IdentifierTable *identifierTable; + + QV4::Debugging::Debugger *debugger; + + Object *globalObject; + + Function *globalCode; + + QV8Engine *v8Engine; + + Value objectCtor; + Value stringCtor; + Value numberCtor; + Value booleanCtor; + Value arrayCtor; + Value functionCtor; + Value dateCtor; + Value regExpCtor; + Value errorCtor; + Value evalErrorCtor; + Value rangeErrorCtor; + Value referenceErrorCtor; + Value syntaxErrorCtor; + Value typeErrorCtor; + Value uRIErrorCtor; + + ObjectPrototype *objectPrototype; + StringPrototype *stringPrototype; + NumberPrototype *numberPrototype; + BooleanPrototype *booleanPrototype; + ArrayPrototype *arrayPrototype; + FunctionPrototype *functionPrototype; + DatePrototype *datePrototype; + RegExpPrototype *regExpPrototype; + ErrorPrototype *errorPrototype; + EvalErrorPrototype *evalErrorPrototype; + RangeErrorPrototype *rangeErrorPrototype; + ReferenceErrorPrototype *referenceErrorPrototype; + SyntaxErrorPrototype *syntaxErrorPrototype; + TypeErrorPrototype *typeErrorPrototype; + URIErrorPrototype *uRIErrorPrototype; + + VariantPrototype *variantPrototype; + SequencePrototype *sequencePrototype; + + QQmlJS::MemoryPool classPool; + InternalClass *emptyClass; + InternalClass *arrayClass; + + EvalFunction *evalFunction; + + QVector argumentsAccessors; + + String *id_undefined; + String *id_null; + String *id_true; + String *id_false; + String *id_boolean; + String *id_number; + String *id_string; + String *id_object; + String *id_function; + String *id_length; + String *id_prototype; + String *id_constructor; + String *id_arguments; + String *id_caller; + String *id_this; + String *id___proto__; + String *id_enumerable; + String *id_configurable; + String *id_writable; + String *id_value; + String *id_get; + String *id_set; + String *id_eval; + String *id_uintMax; + String *id_name; + + mutable QVector functions; + mutable bool functionsNeedSort; + + quint32 m_engineId; + + RegExpCache *regExpCache; + + // Scarce resources are "exceptionally high cost" QVariant types where allowing the + // normal JavaScript GC to clean them up is likely to lead to out-of-memory or other + // out-of-resource situations. When such a resource is passed into JavaScript we + // add it to the scarceResources list and it is destroyed when we return from the + // JavaScript execution that created it. The user can prevent this behavior by + // calling preserve() on the object which removes it from this scarceResource list. + class ScarceResourceData { + public: + ScarceResourceData(const QVariant &data) : data(data) {} + QVariant data; + QIntrusiveListNode node; + }; + QIntrusiveList scarceResources; + + // Normally the JS wrappers for QObjects are stored in the QQmlData/QObjectPrivate, + // but any time a QObject is wrapped a second time in another engine, we have to do + // bookkeeping. + MultiplyWrappedQObjectMap *m_multiplyWrappedQObjects; + + ExecutionEngine(QQmlJS::EvalISelFactory *iselFactory = 0); + ~ExecutionEngine(); + + void enableDebugger(); + + WithContext *newWithContext(Object *with); + CatchContext *newCatchContext(String* exceptionVarName, const QV4::Value &exceptionValue); + CallContext *newCallContext(FunctionObject *f, const QV4::Value &thisObject, QV4::Value *args, int argc); + CallContext *newCallContext(void *stackSpace, FunctionObject *f, const QV4::Value &thisObject, QV4::Value *args, int argc); + CallContext *newQmlContext(FunctionObject *f, Object *qml); + ExecutionContext *pushGlobalContext(); + void pushContext(SimpleCallContext *context); + ExecutionContext *popContext(); + + Function *newFunction(const QString &name); + + FunctionObject *newBuiltinFunction(ExecutionContext *scope, String *name, Value (*code)(SimpleCallContext *)); + FunctionObject *newScriptFunction(ExecutionContext *scope, Function *function); + BoundFunction *newBoundFunction(ExecutionContext *scope, FunctionObject *target, Value boundThis, const QVector &boundArgs); + + Object *newObject(); + Object *newObject(InternalClass *internalClass); + + String *newString(const QString &s); + String *newIdentifier(const QString &text); + + Object *newStringObject(const Value &value); + Object *newNumberObject(const Value &value); + Object *newBooleanObject(const Value &value); + + ArrayObject *newArrayObject(int count = 0); + ArrayObject *newArrayObject(const QStringList &list); + + DateObject *newDateObject(const Value &value); + DateObject *newDateObject(const QDateTime &dt); + + RegExpObject *newRegExpObject(const QString &pattern, int flags); + RegExpObject *newRegExpObject(RegExp* re, bool global); + RegExpObject *newRegExpObject(const QRegExp &re); + + Object *newErrorObject(const Value &value); + Object *newSyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *message); + Object *newSyntaxErrorObject(const QString &message); + Object *newReferenceErrorObject(const QString &message); + Object *newReferenceErrorObject(const QString &message, const QString &fileName, int lineNumber); + Object *newTypeErrorObject(const QString &message); + Object *newRangeErrorObject(const QString &message); + Object *newURIErrorObject(Value message); + + Object *newVariantObject(const QVariant &v); + + Object *newForEachIteratorObject(ExecutionContext *ctx, Object *o); + + Object *qmlContextObject() const; + + struct StackFrame { + QString source; + QString function; + int line; + int column; + }; + typedef QVector StackTrace; + StackTrace stackTrace(int frameLimit = -1) const; + StackFrame currentStackFrame() const; + QUrl resolvedUrl(const QString &file); + + void requireArgumentsAccessors(int n); + + void markObjects(); + + void initRootContext(); + + InternalClass *newClass(const InternalClass &other); + + Function *functionForProgramCounter(quintptr pc) const; + + QmlExtensions *qmlExtensions(); + +private: + QmlExtensions *m_qmlExtensions; +}; + +inline void ExecutionEngine::pushContext(SimpleCallContext *context) +{ + context->parent = current; + current = context; + current->currentEvalCode = 0; +} + +inline ExecutionContext *ExecutionEngine::popContext() +{ + CallContext *c = current->asCallContext(); + if (c && !c->needsOwnArguments()) { + c->arguments = 0; + c->argumentCount = 0; + } + + current = current->parent; + return current; +} + +} // namespace QV4 + +QT_END_NAMESPACE + +#endif // QV4ENGINE_H diff --git a/src/qml/jsruntime/qv4errorobject.cpp b/src/qml/jsruntime/qv4errorobject.cpp new file mode 100644 index 0000000000..516a4d37f8 --- /dev/null +++ b/src/qml/jsruntime/qv4errorobject.cpp @@ -0,0 +1,351 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qv4errorobject_p.h" +#include "qv4mm_p.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifndef Q_OS_WIN +# include +# ifndef Q_OS_VXWORKS +# include +# else +# include "qplatformdefs.h" +# endif +#else +# include +#endif + +using namespace QV4; + +ErrorObject::ErrorObject(ExecutionEngine *engine, const Value &message, ErrorType t) + : Object(engine) + , stack(0) +{ + type = Type_ErrorObject; + vtbl = &static_vtbl; + subtype = t; + defineAccessorProperty(engine, QStringLiteral("stack"), ErrorObject::method_get_stack, 0); + + if (!message.isUndefined()) + defineDefaultProperty(engine->newString(QStringLiteral("message")), message); + defineDefaultProperty(engine, QStringLiteral("name"), Value::fromString(engine, className())); + + stackTrace = engine->stackTrace(); + if (!stackTrace.isEmpty()) { + defineDefaultProperty(engine, QStringLiteral("fileName"), Value::fromString(engine, stackTrace.at(0).source)); + defineDefaultProperty(engine, QStringLiteral("lineNumber"), Value::fromInt32(stackTrace.at(0).line)); + } +} + +Value ErrorObject::method_get_stack(SimpleCallContext *ctx) +{ + ErrorObject *This = ctx->thisObject.asErrorObject(); + if (!This) + ctx->throwTypeError(); + if (!This->stack) { + QString trace; + for (int i = 0; i < This->stackTrace.count(); ++i) { + if (i > 0) + trace += QLatin1Char('\n'); + const ExecutionEngine::StackFrame &frame = This->stackTrace[i]; + trace += frame.function; + trace += QLatin1Char('@'); + trace += frame.source; + if (frame.line >= 0) { + trace += QLatin1Char(':'); + trace += QString::number(frame.line); + } + } + This->stack = ctx->engine->newString(trace); + } + return Value::fromString(This->stack); +} + +void ErrorObject::markObjects(Managed *that) +{ + ErrorObject *This = that->asErrorObject(); + if (This->stack) + This->stack->mark(); + Object::markObjects(that); +} + +DEFINE_MANAGED_VTABLE(ErrorObject); + +DEFINE_MANAGED_VTABLE(SyntaxErrorObject); + +SyntaxErrorObject::SyntaxErrorObject(ExecutionEngine *engine, const Value &msg) + : ErrorObject(engine, msg, SyntaxError) + , msg(0) +{ + vtbl = &static_vtbl; + prototype = engine->syntaxErrorPrototype; +} + +SyntaxErrorObject::SyntaxErrorObject(ExecutionEngine *engine, const QString &msg) + : ErrorObject(engine, Value::fromString(engine, msg), SyntaxError) + , msg(0) +{ + vtbl = &static_vtbl; + prototype = engine->syntaxErrorPrototype; +} + +SyntaxErrorObject::SyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *message) + : ErrorObject(ctx->engine, message ? Value::fromString(message->buildFullMessage(ctx)) : ctx->argument(0), SyntaxError) + , msg(message) +{ + vtbl = &static_vtbl; + prototype = ctx->engine->syntaxErrorPrototype; + if (message) { + defineDefaultProperty(ctx->engine, QStringLiteral("fileName"), Value::fromString(ctx, message->fileName)); + defineDefaultProperty(ctx->engine, QStringLiteral("lineNumber"), Value::fromInt32(message->startLine)); + } +} + + + +EvalErrorObject::EvalErrorObject(ExecutionEngine *engine, const Value &message) + : ErrorObject(engine, message, EvalError) +{ + prototype = engine->evalErrorPrototype; +} + +RangeErrorObject::RangeErrorObject(ExecutionEngine *engine, const Value &message) + : ErrorObject(engine, message, RangeError) +{ + prototype = engine->rangeErrorPrototype; +} + +RangeErrorObject::RangeErrorObject(ExecutionEngine *engine, const QString &message) + : ErrorObject(engine, Value::fromString(engine, message), RangeError) +{ + prototype = engine->rangeErrorPrototype; +} + +ReferenceErrorObject::ReferenceErrorObject(ExecutionEngine *engine, const Value &message) + : ErrorObject(engine, message, ReferenceError) +{ + prototype = engine->referenceErrorPrototype; +} + +ReferenceErrorObject::ReferenceErrorObject(ExecutionEngine *engine, const QString &message) + : ErrorObject(engine, Value::fromString(engine, message), ReferenceError) +{ + prototype = engine->referenceErrorPrototype; +} + +ReferenceErrorObject::ReferenceErrorObject(ExecutionEngine *engine, const QString &msg, const QString &fileName, int lineNumber) + : ErrorObject(engine, Value::fromString(engine, msg), ReferenceError) +{ + prototype = engine->referenceErrorPrototype; + defineDefaultProperty(engine, QStringLiteral("fileName"), Value::fromString(engine->rootContext, fileName)); + defineDefaultProperty(engine, QStringLiteral("lineNumber"), Value::fromInt32(lineNumber)); +} + +TypeErrorObject::TypeErrorObject(ExecutionEngine *engine, const Value &message) + : ErrorObject(engine, message, TypeError) +{ + prototype = engine->typeErrorPrototype; +} + +TypeErrorObject::TypeErrorObject(ExecutionEngine *engine, const QString &message) + : ErrorObject(engine, Value::fromString(engine, message), TypeError) +{ + prototype = engine->typeErrorPrototype; +} + +URIErrorObject::URIErrorObject(ExecutionEngine *engine, const Value &message) + : ErrorObject(engine, message, URIError) +{ + prototype = engine->uRIErrorPrototype; +} + +DEFINE_MANAGED_VTABLE(ErrorCtor); +DEFINE_MANAGED_VTABLE(EvalErrorCtor); +DEFINE_MANAGED_VTABLE(RangeErrorCtor); +DEFINE_MANAGED_VTABLE(ReferenceErrorCtor); +DEFINE_MANAGED_VTABLE(SyntaxErrorCtor); +DEFINE_MANAGED_VTABLE(TypeErrorCtor); +DEFINE_MANAGED_VTABLE(URIErrorCtor); + +ErrorCtor::ErrorCtor(ExecutionContext *scope) + : FunctionObject(scope, scope->engine->newIdentifier(QStringLiteral("Error"))) +{ + vtbl = &static_vtbl; +} + +ErrorCtor::ErrorCtor(ExecutionContext *scope, String *name) + : FunctionObject(scope, name) +{ + vtbl = &static_vtbl; +} + +Value ErrorCtor::construct(Managed *m, Value *args, int argc) +{ + return Value::fromObject(m->engine()->newErrorObject(argc ? args[0] : Value::undefinedValue())); +} + +Value ErrorCtor::call(Managed *that, const Value &, Value *args, int argc) +{ + return that->construct(args, argc); +} + +EvalErrorCtor::EvalErrorCtor(ExecutionContext *scope) + : ErrorCtor(scope, scope->engine->newIdentifier("EvalError")) +{ + vtbl = &static_vtbl; +} + +Value EvalErrorCtor::construct(Managed *m, Value *args, int argc) +{ + return Value::fromObject(new (m->engine()->memoryManager) EvalErrorObject(m->engine(), argc ? args[0] : Value::undefinedValue())); +} + +RangeErrorCtor::RangeErrorCtor(ExecutionContext *scope) + : ErrorCtor(scope, scope->engine->newIdentifier("RangeError")) +{ + vtbl = &static_vtbl; +} + +Value RangeErrorCtor::construct(Managed *m, Value *args, int argc) +{ + return Value::fromObject(new (m->engine()->memoryManager) RangeErrorObject(m->engine(), argc ? args[0] : Value::undefinedValue())); +} + +ReferenceErrorCtor::ReferenceErrorCtor(ExecutionContext *scope) + : ErrorCtor(scope, scope->engine->newIdentifier("ReferenceError")) +{ + vtbl = &static_vtbl; +} + +Value ReferenceErrorCtor::construct(Managed *m, Value *args, int argc) +{ + return Value::fromObject(new (m->engine()->memoryManager) ReferenceErrorObject(m->engine(), argc ? args[0] : Value::undefinedValue())); +} + +SyntaxErrorCtor::SyntaxErrorCtor(ExecutionContext *scope) + : ErrorCtor(scope, scope->engine->newIdentifier("SyntaxError")) +{ + vtbl = &static_vtbl; +} + +Value SyntaxErrorCtor::construct(Managed *m, Value *args, int argc) +{ + return Value::fromObject(new (m->engine()->memoryManager) SyntaxErrorObject(m->engine(), argc ? args[0] : Value::undefinedValue())); +} + +TypeErrorCtor::TypeErrorCtor(ExecutionContext *scope) + : ErrorCtor(scope, scope->engine->newIdentifier("TypeError")) +{ + vtbl = &static_vtbl; +} + +Value TypeErrorCtor::construct(Managed *m, Value *args, int argc) +{ + return Value::fromObject(new (m->engine()->memoryManager) TypeErrorObject(m->engine(), argc ? args[0] : Value::undefinedValue())); +} + +URIErrorCtor::URIErrorCtor(ExecutionContext *scope) + : ErrorCtor(scope, scope->engine->newIdentifier("URIError")) +{ + vtbl = &static_vtbl; +} + +Value URIErrorCtor::construct(Managed *m, Value *args, int argc) +{ + return Value::fromObject(new (m->engine()->memoryManager) URIErrorObject(m->engine(), argc ? args[0] : Value::undefinedValue())); +} + +void ErrorPrototype::init(ExecutionEngine *engine, const Value &ctor, Object *obj) +{ + ctor.objectValue()->defineReadonlyProperty(engine->id_prototype, Value::fromObject(obj)); + ctor.objectValue()->defineReadonlyProperty(engine->id_length, Value::fromInt32(1)); + obj->defineDefaultProperty(engine, QStringLiteral("constructor"), ctor); + obj->defineDefaultProperty(engine, QStringLiteral("toString"), method_toString, 0); + obj->defineDefaultProperty(engine, QStringLiteral("message"), Value::fromString(engine, QString())); +} + +Value ErrorPrototype::method_toString(SimpleCallContext *ctx) +{ + Object *o = ctx->thisObject.asObject(); + if (!o) + ctx->throwTypeError(); + + Value name = o->get(ctx->engine->newString(QString::fromLatin1("name"))); + QString qname; + if (name.isUndefined()) + qname = QString::fromLatin1("Error"); + else + qname = __qmljs_to_string(name, ctx).stringValue()->toQString(); + + Value message = o->get(ctx->engine->newString(QString::fromLatin1("message"))); + QString qmessage; + if (!message.isUndefined()) + qmessage = __qmljs_to_string(message, ctx).stringValue()->toQString(); + + QString str; + if (qname.isEmpty()) { + str = qmessage; + } else if (qmessage.isEmpty()) { + str = qname; + } else { + str = qname + QLatin1String(": ") + qmessage; + } + + return Value::fromString(ctx, str); +} diff --git a/src/qml/jsruntime/qv4errorobject_p.h b/src/qml/jsruntime/qv4errorobject_p.h new file mode 100644 index 0000000000..d3e0f107bc --- /dev/null +++ b/src/qml/jsruntime/qv4errorobject_p.h @@ -0,0 +1,246 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4ERROROBJECT_H +#define QV4ERROROBJECT_H + +#include "qv4object_p.h" +#include "qv4functionobject_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct SyntaxErrorObject; + +struct ErrorObject: Object { + Q_MANAGED + + enum ErrorType { + Error, + EvalError, + RangeError, + ReferenceError, + SyntaxError, + TypeError, + URIError + }; + + ErrorObject(ExecutionEngine *engine, const Value &message, ErrorType t = Error); + + SyntaxErrorObject *asSyntaxError(); + + ExecutionEngine::StackTrace stackTrace; + String *stack; + + static Value method_get_stack(SimpleCallContext *ctx); + static void markObjects(Managed *that); + static void destroy(Managed *that) { static_cast(that)->~ErrorObject(); } +}; + +struct EvalErrorObject: ErrorObject { + EvalErrorObject(ExecutionEngine *engine, const Value &message); +}; + +struct RangeErrorObject: ErrorObject { + RangeErrorObject(ExecutionEngine *engine, const Value &message); + RangeErrorObject(ExecutionEngine *engine, const QString &msg); +}; + +struct ReferenceErrorObject: ErrorObject { + ReferenceErrorObject(ExecutionEngine *engine, const Value &message); + ReferenceErrorObject(ExecutionEngine *engine, const QString &msg); + ReferenceErrorObject(ExecutionEngine *engine, const QString &msg, const QString &fileName, int lineNumber); +}; + +struct SyntaxErrorObject: ErrorObject { + SyntaxErrorObject(ExecutionEngine *engine, const Value &msg); + SyntaxErrorObject(ExecutionEngine *engine, const QString &msg); + SyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *msg); + ~SyntaxErrorObject() { delete msg; } + static void destroy(Managed *that) { static_cast(that)->~SyntaxErrorObject(); } + + DiagnosticMessage *message() { return msg; } + +private: + DiagnosticMessage *msg; +protected: + static const ManagedVTable static_vtbl; +}; + +struct TypeErrorObject: ErrorObject { + TypeErrorObject(ExecutionEngine *engine, const Value &message); + TypeErrorObject(ExecutionEngine *engine, const QString &msg); +}; + +struct URIErrorObject: ErrorObject { + URIErrorObject(ExecutionEngine *engine, const Value &message); +}; + +struct ErrorCtor: FunctionObject +{ + ErrorCtor(ExecutionContext *scope); + ErrorCtor(ExecutionContext *scope, String *name); + + static Value construct(Managed *, Value *args, int argc); + static Value call(Managed *that, const Value &, Value *, int); + +protected: + static const ManagedVTable static_vtbl; +}; + +struct EvalErrorCtor: ErrorCtor +{ + EvalErrorCtor(ExecutionContext *scope); + + static Value construct(Managed *m, Value *args, int argc); + +protected: + static const ManagedVTable static_vtbl; +}; + +struct RangeErrorCtor: ErrorCtor +{ + RangeErrorCtor(ExecutionContext *scope); + + static Value construct(Managed *m, Value *args, int argc); + +protected: + static const ManagedVTable static_vtbl; +}; + +struct ReferenceErrorCtor: ErrorCtor +{ + ReferenceErrorCtor(ExecutionContext *scope); + + static Value construct(Managed *m, Value *args, int argc); + +protected: + static const ManagedVTable static_vtbl; +}; + +struct SyntaxErrorCtor: ErrorCtor +{ + SyntaxErrorCtor(ExecutionContext *scope); + + static Value construct(Managed *m, Value *args, int argc); + +protected: + static const ManagedVTable static_vtbl; +}; + +struct TypeErrorCtor: ErrorCtor +{ + TypeErrorCtor(ExecutionContext *scope); + + static Value construct(Managed *m, Value *args, int argc); + +protected: + static const ManagedVTable static_vtbl; +}; + +struct URIErrorCtor: ErrorCtor +{ + URIErrorCtor(ExecutionContext *scope); + + static Value construct(Managed *m, Value *args, int argc); + +protected: + static const ManagedVTable static_vtbl; +}; + + +struct ErrorPrototype: ErrorObject +{ + // ### shouldn't be undefined + ErrorPrototype(ExecutionEngine *engine): ErrorObject(engine, Value::undefinedValue()) {} + void init(ExecutionEngine *engine, const Value &ctor) { init(engine, ctor, this); } + + static void init(ExecutionEngine *engine, const Value &ctor, Object *obj); + static Value method_toString(SimpleCallContext *ctx); +}; + +struct EvalErrorPrototype: EvalErrorObject +{ + EvalErrorPrototype(ExecutionEngine *engine): EvalErrorObject(engine, Value::undefinedValue()) { vtbl = &static_vtbl; } + void init(ExecutionEngine *engine, const Value &ctor) { ErrorPrototype::init(engine, ctor, this); } +}; + +struct RangeErrorPrototype: RangeErrorObject +{ + RangeErrorPrototype(ExecutionEngine *engine): RangeErrorObject(engine, Value::undefinedValue()) { vtbl = &static_vtbl; } + void init(ExecutionEngine *engine, const Value &ctor) { ErrorPrototype::init(engine, ctor, this); } +}; + +struct ReferenceErrorPrototype: ReferenceErrorObject +{ + ReferenceErrorPrototype(ExecutionEngine *engine): ReferenceErrorObject(engine, Value::undefinedValue()) { vtbl = &static_vtbl; } + void init(ExecutionEngine *engine, const Value &ctor) { ErrorPrototype::init(engine, ctor, this); } +}; + +struct SyntaxErrorPrototype: SyntaxErrorObject +{ + SyntaxErrorPrototype(ExecutionEngine *engine): SyntaxErrorObject(engine, 0) { vtbl = &static_vtbl; } + void init(ExecutionEngine *engine, const Value &ctor) { ErrorPrototype::init(engine, ctor, this); } +}; + +struct TypeErrorPrototype: TypeErrorObject +{ + TypeErrorPrototype(ExecutionEngine *engine): TypeErrorObject(engine, Value::undefinedValue()) { vtbl = &static_vtbl; } + void init(ExecutionEngine *engine, const Value &ctor) { ErrorPrototype::init(engine, ctor, this); } +}; + +struct URIErrorPrototype: URIErrorObject +{ + URIErrorPrototype(ExecutionEngine *engine): URIErrorObject(engine, Value::undefinedValue()) { vtbl = &static_vtbl; } + void init(ExecutionEngine *engine, const Value &ctor) { ErrorPrototype::init(engine, ctor, this); } +}; + + +inline SyntaxErrorObject *ErrorObject::asSyntaxError() +{ + return subtype == SyntaxError ? static_cast(this) : 0; +} + +} + +QT_END_NAMESPACE + +#endif // QV4ECMAOBJECTS_P_H diff --git a/src/qml/jsruntime/qv4exception.cpp b/src/qml/jsruntime/qv4exception.cpp new file mode 100644 index 0000000000..9f15c27ffc --- /dev/null +++ b/src/qml/jsruntime/qv4exception.cpp @@ -0,0 +1,129 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4exception_p.h" +#include "qv4errorobject_p.h" +#include "qv4debugging_p.h" +#include "qv4unwindhelper_p.h" + +#include + +#if USE(LIBUNWIND_DEBUG) +#include +#include +#endif + +QT_BEGIN_NAMESPACE + +using namespace QV4; + + +void Exception::throwException(ExecutionContext *context, const Value &value) +{ + if (context->engine->debugger) + context->engine->debugger->aboutToThrow(value); + + UnwindHelper::prepareForUnwind(context); + +#if USE(LIBUNWIND_DEBUG) + printf("about to throw exception. walking stack first with libunwind:\n"); + unw_cursor_t cursor; unw_context_t uc; + unw_word_t ip, sp; + + unw_getcontext(&uc); + unw_init_local(&cursor, &uc); + while (unw_step(&cursor) > 0) { + unw_get_reg(&cursor, UNW_REG_IP, &ip); + unw_get_reg(&cursor, UNW_REG_SP, &sp); + printf("ip = %lx, sp = %lx ", (long) ip, (long) sp); + void * const addr = (void*)ip; + char **symbol = backtrace_symbols(&addr, 1); + printf("%s", symbol[0]); + free(symbol); + printf("\n"); + } + printf("stack walked. throwing exception now...\n"); +#endif + + throwInternal(context, value); +} + +Exception::Exception(ExecutionContext *throwingContext, const Value &exceptionValue) + : exception(exceptionValue) +{ + this->throwingContext = throwingContext->engine->current; + accepted = false; + if (ErrorObject *error = exceptionValue.asErrorObject()) + m_stackTrace = error->stackTrace; + else + m_stackTrace = throwingContext->engine->stackTrace(); +} + +Exception::~Exception() +{ + assert(accepted); +} + +void Exception::accept(ExecutionContext *catchingContext) +{ + assert(!accepted); + accepted = true; + partiallyUnwindContext(catchingContext); +} + +void Exception::partiallyUnwindContext(ExecutionContext *catchingContext) +{ + if (!throwingContext) + return; + ExecutionContext *context = throwingContext; + while (context != catchingContext) + context = context->engine->popContext(); + throwingContext = context; +} + +#if !defined(V4_CXX_ABI_EXCEPTION) +void Exception::throwInternal(ExecutionContext *throwingContext, const Value &exceptionValue) +{ + throw Exception(throwingContext, exceptionValue); +} +#endif + +QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4exception_gcc.cpp b/src/qml/jsruntime/qv4exception_gcc.cpp new file mode 100644 index 0000000000..0324a06e0b --- /dev/null +++ b/src/qml/jsruntime/qv4exception_gcc.cpp @@ -0,0 +1,143 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4exception_p.h" + +#include +#include +#include +#include +#include + +/* + * This is a little bit hacky as it relies on the fact that exceptions are + * reference counted in libstdc++ and that affects the layout of the standardized + * cxa_exception, making it bigger. LLVM's libcxxabi stores the reference count + * differently, so this here is entirely GNU libstdc++ specific. + * + * Eliminating this dependency is doable but requires replacing the use of C++ exceptions + * with foreign exceptions (a different exception class) and then using __cxa_get_globals + * to get hold of the exception inside the catch (...). AFAICS that would be portable. + */ + +namespace { + +// 2.1.1 from http://mentorembedded.github.io/cxx-abi/abi-eh.html +struct cxa_exception { + std::type_info *typeInfo; + void (*exceptionDestructor)(void*); + std::unexpected_handler unexpectedHandler; + std::terminate_handler terminateHandler; + cxa_exception *nextException; + int handlerCount; +#ifdef __ARM_EABI_UNWINDER__ + cxa_exception *nextPropagatingException; + int propagationCount; +#else + int handlerSwitchValue; + const char *actionRecord; + const char *languageSpecificData; + void *catchTemp; + void *adjustedPtr; +#endif + _Unwind_Exception unwindHeader; +}; + +// This is what libstdc++ actually allocates +struct gcc_refcounted_compatible_exception { + _Atomic_word refCount; + cxa_exception x; +}; + +} + +static void exception_cleanup(_Unwind_Reason_Code, _Unwind_Exception *ex) +{ + gcc_refcounted_compatible_exception *exception = reinterpret_cast(ex + 1) - 1; + if (!--exception->refCount) { + if (exception->x.exceptionDestructor) + exception->x.exceptionDestructor(ex + 1); + abi::__cxa_free_exception(ex + 1); + } +} + +static void exception_destructor(void *ex) +{ + reinterpret_cast(ex)->~Exception(); +} + +QT_BEGIN_NAMESPACE + +using namespace QV4; + +void Exception::throwInternal(ExecutionContext *throwingContext, const Value &exceptionValue) +{ + void *rawException = abi::__cxa_allocate_exception(sizeof(QV4::Exception)); + gcc_refcounted_compatible_exception *refCountedException = reinterpret_cast(rawException) - 1; + cxa_exception *exception = &refCountedException->x; + + (void)new (rawException) Exception(throwingContext, exceptionValue); + + refCountedException->refCount = 1; + exception->typeInfo = const_cast(&typeid(Exception)); + exception->exceptionDestructor = &exception_destructor; + exception->unexpectedHandler = std::unexpected; + exception->terminateHandler = std::terminate; + exception->unwindHeader.exception_cleanup = &exception_cleanup; +#ifdef __ARM_EABI_UNWINDER__ + exception->unwindHeader.exception_class[0] = 'G'; + exception->unwindHeader.exception_class[1] = 'N'; + exception->unwindHeader.exception_class[2] = 'U'; + exception->unwindHeader.exception_class[3] = 'C'; + exception->unwindHeader.exception_class[4] = 'C'; + exception->unwindHeader.exception_class[5] = '+'; + exception->unwindHeader.exception_class[6] = '+'; + exception->unwindHeader.exception_class[7] = 0; +#else + exception->unwindHeader.exception_class = 0x474e5543432b2b00; // GNUCC++0 +#endif + + _Unwind_RaiseException(&exception->unwindHeader); + abi::__cxa_begin_catch(rawException); + std::terminate(); +} + +QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4exception_p.h b/src/qml/jsruntime/qv4exception_p.h new file mode 100644 index 0000000000..8ba06f57f4 --- /dev/null +++ b/src/qml/jsruntime/qv4exception_p.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4EXCEPTION_GNU_P +#define QV4EXCEPTION_GNU_P + +#include +#include "qv4value_p.h" +#include "qv4engine_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct Q_QML_EXPORT Exception { + static void throwException(ExecutionContext *throwingContext, const Value &exceptionValue); + + ~Exception(); + + void accept(ExecutionContext *catchingContext); + + void partiallyUnwindContext(ExecutionContext *catchingContext); + + Value value() const { return exception; } + + ExecutionEngine::StackTrace stackTrace() const { return m_stackTrace; } + +private: + void *operator new(size_t, void *p) { return p; } + + explicit Exception(ExecutionContext *throwingContext, const Value &exceptionValue); + + ExecutionContext *throwingContext; + bool accepted; + PersistentValue exception; + ExecutionEngine::StackTrace m_stackTrace; + static void throwInternal(ExecutionContext *throwingContext, const Value &exceptionValue); +}; + +} // namespace QV4 + +QT_END_NAMESPACE + +#endif // QV4EXCEPTION_GNU_P diff --git a/src/qml/jsruntime/qv4executableallocator.cpp b/src/qml/jsruntime/qv4executableallocator.cpp new file mode 100644 index 0000000000..a754663556 --- /dev/null +++ b/src/qml/jsruntime/qv4executableallocator.cpp @@ -0,0 +1,220 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4executableallocator_p.h" + +#include +#include +#include + +using namespace QV4; + +void *ExecutableAllocator::Allocation::start() const +{ + return reinterpret_cast(addr); +} + +ExecutableAllocator::Allocation *ExecutableAllocator::Allocation::split(size_t dividingSize) +{ + Allocation *remainder = new Allocation; + if (next) + next->prev = remainder; + + remainder->next = next; + next = remainder; + + remainder->prev = this; + + remainder->size = size - dividingSize; + remainder->free = free; + remainder->addr = addr + dividingSize; + size = dividingSize; + + return remainder; +} + +bool ExecutableAllocator::Allocation::mergeNext(ExecutableAllocator *allocator) +{ + assert(free); + if (!next || !next->free) + return false; + + allocator->freeAllocations.remove(size, this); + allocator->freeAllocations.remove(next->size, next); + + size += next->size; + Allocation *newNext = next->next; + delete next; + next = newNext; + if (next) + next->prev = this; + + allocator->freeAllocations.insert(size, this); + return true; +} + +bool ExecutableAllocator::Allocation::mergePrevious(ExecutableAllocator *allocator) +{ + assert(free); + if (!prev || !prev->free) + return false; + + allocator->freeAllocations.remove(size, this); + allocator->freeAllocations.remove(prev->size, prev); + + prev->size += size; + if (next) + next->prev = prev; + prev->next = next; + + allocator->freeAllocations.insert(prev->size, prev); + + delete this; + return true; +} + +ExecutableAllocator::ChunkOfPages::~ChunkOfPages() +{ + delete unwindInfo; + Allocation *alloc = firstAllocation; + while (alloc) { + Allocation *next = alloc->next; + delete alloc; + alloc = next; + } + pages->deallocate(); + delete pages; +} + +bool ExecutableAllocator::ChunkOfPages::contains(Allocation *alloc) const +{ + Allocation *it = firstAllocation; + while (it) { + if (it == alloc) + return true; + it = it->next; + } + return false; +} + +ExecutableAllocator::~ExecutableAllocator() +{ + qDeleteAll(chunks); +} + +ExecutableAllocator::Allocation *ExecutableAllocator::allocate(size_t size) +{ + Allocation *allocation = 0; + + // Code is best aligned to 16-byte boundaries. + size = WTF::roundUpToMultipleOf(16, size); + + QMultiMap::Iterator it = freeAllocations.lowerBound(size); + if (it != freeAllocations.end()) { + allocation = *it; + freeAllocations.erase(it); + } + + if (!allocation) { + ChunkOfPages *chunk = new ChunkOfPages; + size_t allocSize = WTF::roundUpToMultipleOf(WTF::pageSize(), size); + chunk->pages = new WTF::PageAllocation(WTF::PageAllocation::allocate(allocSize, OSAllocator::JSJITCodePages)); + chunks.insert(reinterpret_cast(chunk->pages->base()) - 1, chunk); + allocation = new Allocation; + allocation->addr = reinterpret_cast(chunk->pages->base()); + allocation->size = allocSize; + allocation->free = true; + chunk->firstAllocation = allocation; + } + + assert(allocation); + assert(allocation->free); + + allocation->free = false; + + if (allocation->size > size) { + Allocation *remainder = allocation->split(size); + remainder->free = true; + if (!remainder->mergeNext(this)) + freeAllocations.insert(remainder->size, remainder); + } + + return allocation; +} + +void ExecutableAllocator::free(Allocation *allocation) +{ + assert(allocation); + + allocation->free = true; + + QMap::Iterator it = chunks.lowerBound(allocation->addr); + if (it != chunks.begin()) + --it; + assert(it != chunks.end()); + ChunkOfPages *chunk = *it; + assert(chunk->contains(allocation)); + + bool merged = allocation->mergeNext(this); + merged |= allocation->mergePrevious(this); + if (!merged) + freeAllocations.insert(allocation->size, allocation); + + allocation = 0; + + if (!chunk->firstAllocation->next) { + freeAllocations.remove(chunk->firstAllocation->size, chunk->firstAllocation); + chunks.erase(it); + delete chunk; + return; + } +} + +ExecutableAllocator::ChunkOfPages *ExecutableAllocator::chunkForAllocation(Allocation *allocation) const +{ + QMap::ConstIterator it = chunks.lowerBound(allocation->addr); + if (it != chunks.begin()) + --it; + if (it == chunks.end()) + return 0; + return *it; +} + diff --git a/src/qml/jsruntime/qv4executableallocator_p.h b/src/qml/jsruntime/qv4executableallocator_p.h new file mode 100644 index 0000000000..2a304baf9c --- /dev/null +++ b/src/qml/jsruntime/qv4executableallocator_p.h @@ -0,0 +1,134 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4EXECUTABLEALLOCATOR_H +#define QV4EXECUTABLEALLOCATOR_H + +#include "qv4global_p.h" + +#include +#include +#include +#include + +namespace WTF { +class PageAllocation; +}; + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +class Q_AUTOTEST_EXPORT ExecutableAllocator +{ +public: + struct ChunkOfPages; + struct Allocation; + + ~ExecutableAllocator(); + + Allocation *allocate(size_t size); + void free(Allocation *allocation); + + struct Allocation + { + Allocation() + : addr(0) + , size(0) + , free(true) + , next(0) + , prev(0) + {} + + void *start() const; + + private: + friend class ExecutableAllocator; + + Allocation *split(size_t dividingSize); + bool mergeNext(ExecutableAllocator *allocator); + bool mergePrevious(ExecutableAllocator *allocator); + + quintptr addr; + uint size : 31; // More than 2GB of function code? nah :) + uint free : 1; + Allocation *next; + Allocation *prev; + }; + + // for debugging / unit-testing + int freeAllocationCount() const { return freeAllocations.count(); } + int chunkCount() const { return chunks.count(); } + + // Used to store CIE+FDE on x86/x86-64. + struct PlatformUnwindInfo + { + virtual ~PlatformUnwindInfo() {} + }; + + struct ChunkOfPages + { + ChunkOfPages() + : pages(0) + , firstAllocation(0) + , unwindInfo(0) + {} + ~ChunkOfPages(); + + WTF::PageAllocation *pages; + Allocation *firstAllocation; + PlatformUnwindInfo *unwindInfo; + + bool contains(Allocation *alloc) const; + }; + + ChunkOfPages *chunkForAllocation(Allocation *allocation) const; + +private: + QMultiMap freeAllocations; + QMap chunks; +}; + +} + +QT_END_NAMESPACE + +#endif // QV4EXECUTABLEALLOCATOR_H diff --git a/src/qml/jsruntime/qv4function.cpp b/src/qml/jsruntime/qv4function.cpp new file mode 100644 index 0000000000..bf633a9b41 --- /dev/null +++ b/src/qml/jsruntime/qv4function.cpp @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4function_p.h" +#include "qv4managed_p.h" +#include "qv4string_p.h" +#include "qv4value_p.h" + +QT_BEGIN_NAMESPACE + +using namespace QV4; + +Function::~Function() +{ + delete[] codeData; +} + +void Function::mark() +{ + if (name) + name->mark(); + for (int i = 0; i < formals.size(); ++i) + formals.at(i)->mark(); + for (int i = 0; i < locals.size(); ++i) + locals.at(i)->mark(); + for (int i = 0; i < generatedValues.size(); ++i) + if (Managed *m = generatedValues.at(i).asManaged()) + m->mark(); + for (int i = 0; i < identifiers.size(); ++i) + identifiers.at(i)->mark(); +} + +namespace QV4 { +bool operator<(const LineNumberMapping &mapping, qptrdiff pc) +{ + return mapping.codeOffset < pc; +} +} + +int Function::lineNumberForProgramCounter(qptrdiff offset) const +{ + QVector::ConstIterator it = qLowerBound(lineNumberMappings.begin(), lineNumberMappings.end(), offset); + if (it != lineNumberMappings.constBegin() && lineNumberMappings.count() > 0) + --it; + if (it == lineNumberMappings.constEnd()) + return -1; + return it->lineNumber; +} + +QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4function_p.h b/src/qml/jsruntime/qv4function_p.h new file mode 100644 index 0000000000..4dd0533d51 --- /dev/null +++ b/src/qml/jsruntime/qv4function_p.h @@ -0,0 +1,142 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4FUNCTION_H +#define QV4FUNCTION_H + +#include "qv4global_p.h" + +#include +#include +#include + +#include +#include +#include "qv4value_def_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct String; +struct Function; +struct Object; +struct FunctionObject; +struct ExecutionContext; +struct ExecutionEngine; +class MemoryManager; + +struct ObjectPrototype; +struct StringPrototype; +struct NumberPrototype; +struct BooleanPrototype; +struct ArrayPrototype; +struct FunctionPrototype; +struct DatePrototype; +struct ErrorPrototype; +struct EvalErrorPrototype; +struct RangeErrorPrototype; +struct ReferenceErrorPrototype; +struct SyntaxErrorPrototype; +struct TypeErrorPrototype; +struct URIErrorPrototype; +struct InternalClass; +struct Lookup; + +struct LineNumberMapping +{ + quint32 codeOffset; + int lineNumber; +}; + +struct Function { + String *name; + + Value (*code)(ExecutionContext *, const uchar *); + const uchar *codeData; + JSC::MacroAssemblerCodeRef codeRef; + quint32 codeSize; + + QVector formals; + QVector locals; + QVector generatedValues; + QVector identifiers; + QVector nestedFunctions; + Function *outer; + + Lookup *lookups; + + bool hasNestedFunctions; + bool hasDirectEval; + bool usesArgumentsObject; + bool isStrict; + bool isNamedExpression; + + QString sourceFile; + QVector lineNumberMappings; + + Function(String *name) + : name(name) + , code(0) + , codeData(0) + , codeSize(0) + , outer(0) + , lookups(0) + , hasNestedFunctions(0) + , hasDirectEval(false) + , usesArgumentsObject(false) + , isStrict(false) + , isNamedExpression(false) + {} + ~Function(); + + inline bool needsActivation() const { return hasNestedFunctions || hasDirectEval || usesArgumentsObject; } + + void mark(); + + int lineNumberForProgramCounter(qptrdiff offset) const; +}; + +} +Q_DECLARE_TYPEINFO(QV4::LineNumberMapping, Q_PRIMITIVE_TYPE); + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp new file mode 100644 index 0000000000..ffdd08ed0a --- /dev/null +++ b/src/qml/jsruntime/qv4functionobject.cpp @@ -0,0 +1,558 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4object_p.h" +#include "qv4jsir_p.h" +#include "qv4isel_p.h" +#include "qv4objectproto_p.h" +#include "qv4stringobject_p.h" +#include "qv4function_p.h" +#include "qv4mm_p.h" +#include "qv4exception_p.h" + +#include +#include +#include +#include +#include +#include +#include "private/qlocale_tools_p.h" + +#include +#include +#include +#include +#include +#include "qv4alloca_p.h" + +using namespace QV4; + + +DEFINE_MANAGED_VTABLE(FunctionObject); + +FunctionObject::FunctionObject(ExecutionContext *scope, String *name) + : Object(scope->engine) + , scope(scope) + , name(name) + , formalParameterList(0) + , varList(0) + , formalParameterCount(0) + , varCount(0) + , function(0) +{ + vtbl = &static_vtbl; + prototype = scope->engine->functionPrototype; + + type = Type_FunctionObject; + needsActivation = true; + usesArgumentsObject = false; + strictMode = false; +#ifndef QT_NO_DEBUG + assert(scope->next != (ExecutionContext *)0x1); +#endif + + if (name) + defineReadonlyProperty(scope->engine->id_name, Value::fromString(name)); +} + +Value FunctionObject::newInstance() +{ + return construct(0, 0); +} + +bool FunctionObject::hasInstance(Managed *that, const Value &value) +{ + FunctionObject *f = static_cast(that); + + Object *v = value.asObject(); + if (!v) + return false; + + ExecutionContext *ctx = f->engine()->current; + Object *o = f->get(ctx->engine->id_prototype).asObject(); + if (!o) + ctx->throwTypeError(); + + while (v) { + v = v->prototype; + + if (! v) + break; + else if (o == v) + return true; + } + + return false; +} + +Value FunctionObject::construct(Managed *that, Value *, int) +{ + FunctionObject *f = static_cast(that); + ExecutionEngine *v4 = f->engine(); + + Object *obj = v4->newObject(); + Value proto = f->get(v4->id_prototype); + if (proto.isObject()) + obj->prototype = proto.objectValue(); + return Value::fromObject(obj); +} + +Value FunctionObject::call(Managed *, const Value &, Value *, int) +{ + return Value::undefinedValue(); +} + +void FunctionObject::markObjects(Managed *that) +{ + FunctionObject *o = static_cast(that); + if (o->name) + o->name->mark(); + // these are marked in VM::Function: +// for (uint i = 0; i < formalParameterCount; ++i) +// formalParameterList[i]->mark(); +// for (uint i = 0; i < varCount; ++i) +// varList[i]->mark(); + o->scope->mark(); + if (o->function) + o->function->mark(); + + Object::markObjects(that); +} + + +DEFINE_MANAGED_VTABLE(FunctionCtor); + +FunctionCtor::FunctionCtor(ExecutionContext *scope) + : FunctionObject(scope, scope->engine->newIdentifier(QStringLiteral("Function"))) +{ + vtbl = &static_vtbl; +} + +// 15.3.2 +Value FunctionCtor::construct(Managed *that, Value *args, int argc) +{ + FunctionCtor *f = static_cast(that); + MemoryManager::GCBlocker gcBlocker(f->engine()->memoryManager); + + ExecutionContext *ctx = f->engine()->current; + QString arguments; + QString body; + if (argc > 0) { + for (uint i = 0; i < argc - 1; ++i) { + if (i) + arguments += QLatin1String(", "); + arguments += args[i].toString(ctx)->toQString(); + } + body = args[argc - 1].toString(ctx)->toQString(); + } + + QString function = QLatin1String("function(") + arguments + QLatin1String("){") + body + QLatin1String("}"); + + QQmlJS::Engine ee, *engine = ⅇ + QQmlJS::Lexer lexer(engine); + lexer.setCode(function, 1, false); + QQmlJS::Parser parser(engine); + + const bool parsed = parser.parseExpression(); + + if (!parsed) + f->engine()->current->throwSyntaxError(0); + + using namespace QQmlJS::AST; + FunctionExpression *fe = QQmlJS::AST::cast(parser.rootNode()); + ExecutionEngine *v4 = f->engine(); + if (!fe) + v4->current->throwSyntaxError(0); + + QQmlJS::V4IR::Module module; + + QQmlJS::Codegen cg(v4->current, f->strictMode); + QQmlJS::V4IR::Function *irf = cg(QString(), function, fe, &module); + + QScopedPointer isel(v4->iselFactory->create(v4, &module)); + QV4::Function *vmf = isel->vmFunction(irf); + + return Value::fromObject(v4->newScriptFunction(v4->rootContext, vmf)); +} + +// 15.3.1: This is equivalent to new Function(...) +Value FunctionCtor::call(Managed *that, const Value &, Value *args, int argc) +{ + return construct(that, args, argc); +} + +FunctionPrototype::FunctionPrototype(ExecutionContext *ctx) + : FunctionObject(ctx) +{ +} + +void FunctionPrototype::init(ExecutionContext *ctx, const Value &ctor) +{ + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); + + defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(0)); + defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); + defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); + defineDefaultProperty(ctx, QStringLiteral("apply"), method_apply, 2); + defineDefaultProperty(ctx, QStringLiteral("call"), method_call, 1); + defineDefaultProperty(ctx, QStringLiteral("bind"), method_bind, 1); + +} + +Value FunctionPrototype::method_toString(SimpleCallContext *ctx) +{ + FunctionObject *fun = ctx->thisObject.asFunctionObject(); + if (!fun) + ctx->throwTypeError(); + + return Value::fromString(ctx, QStringLiteral("function() { [code] }")); +} + +Value FunctionPrototype::method_apply(SimpleCallContext *ctx) +{ + Value thisArg = ctx->argument(0); + + Value arg = ctx->argument(1); + QVector args; + + if (Object *arr = arg.asObject()) { + quint32 len = arr->get(ctx->engine->id_length).toUInt32(); + for (quint32 i = 0; i < len; ++i) { + Value a = arr->getIndexed(i); + args.append(a); + } + } else if (!(arg.isUndefined() || arg.isNull())) { + ctx->throwTypeError(); + return Value::undefinedValue(); + } + + FunctionObject *o = ctx->thisObject.asFunctionObject(); + if (!o) + ctx->throwTypeError(); + + return o->call(thisArg, args.data(), args.size()); +} + +Value FunctionPrototype::method_call(SimpleCallContext *ctx) +{ + Value thisArg = ctx->argument(0); + + QVector args(ctx->argumentCount ? ctx->argumentCount - 1 : 0); + if (ctx->argumentCount) + qCopy(ctx->arguments + 1, + ctx->arguments + ctx->argumentCount, args.begin()); + + FunctionObject *o = ctx->thisObject.asFunctionObject(); + if (!o) + ctx->throwTypeError(); + + return o->call(thisArg, args.data(), args.size()); +} + +Value FunctionPrototype::method_bind(SimpleCallContext *ctx) +{ + FunctionObject *target = ctx->thisObject.asFunctionObject(); + if (!target) + ctx->throwTypeError(); + + Value boundThis = ctx->argument(0); + QVector boundArgs; + for (uint i = 1; i < ctx->argumentCount; ++i) + boundArgs += ctx->argument(i); + + + BoundFunction *f = ctx->engine->newBoundFunction(ctx->engine->rootContext, target, boundThis, boundArgs); + return Value::fromObject(f); +} + + +static Value throwTypeError(SimpleCallContext *ctx) +{ + ctx->throwTypeError(); + return Value::undefinedValue(); +} + +DEFINE_MANAGED_VTABLE(ScriptFunction); + +ScriptFunction::ScriptFunction(ExecutionContext *scope, Function *function) + : FunctionObject(scope, function->name) +{ + vtbl = &static_vtbl; + this->function = function; + assert(function); + assert(function->code); + + // global function + if (!scope) + return; + + MemoryManager::GCBlocker gcBlocker(scope->engine->memoryManager); + + needsActivation = function->needsActivation(); + usesArgumentsObject = function->usesArgumentsObject; + strictMode = function->isStrict; + formalParameterCount = function->formals.size(); + formalParameterList = function->formals.constData(); + defineReadonlyProperty(scope->engine->id_length, Value::fromInt32(formalParameterCount)); + + varCount = function->locals.size(); + varList = function->locals.constData(); + + Object *proto = scope->engine->newObject(); + proto->defineDefaultProperty(scope->engine->id_constructor, Value::fromObject(this)); + Property *pd = insertMember(scope->engine->id_prototype, Attr_NotEnumerable|Attr_NotConfigurable); + pd->value = Value::fromObject(proto); + + if (scope->strictMode) { + FunctionObject *thrower = scope->engine->newBuiltinFunction(scope, 0, throwTypeError); + Property pd = Property::fromAccessor(thrower, thrower); + __defineOwnProperty__(scope, QStringLiteral("caller"), pd, Attr_Accessor|Attr_NotEnumerable|Attr_NotConfigurable); + __defineOwnProperty__(scope, QStringLiteral("arguments"), pd, Attr_Accessor|Attr_NotEnumerable|Attr_NotConfigurable); + } +} + +Value ScriptFunction::construct(Managed *that, Value *args, int argc) +{ + ScriptFunction *f = static_cast(that); + assert(f->function->code); + ExecutionEngine *v4 = f->engine(); + Object *obj = v4->newObject(); + Value proto = f->get(v4->id_prototype); + if (proto.isObject()) + obj->prototype = proto.objectValue(); + + ExecutionContext *context = v4->current; + quintptr stackSpace[stackContextSize/sizeof(quintptr)]; + ExecutionContext *ctx = v4->newCallContext(stackSpace, f, Value::fromObject(obj), args, argc); + + Value result; + try { + result = f->function->code(ctx, f->function->codeData); + } catch (Exception &ex) { + ex.partiallyUnwindContext(context); + throw; + } + ctx->engine->popContext(); + + if (result.isObject()) + return result; + return Value::fromObject(obj); +} + +Value ScriptFunction::call(Managed *that, const Value &thisObject, Value *args, int argc) +{ + ScriptFunction *f = static_cast(that); + assert(f->function->code); + quintptr stackSpace[stackContextSize/sizeof(quintptr)]; + ExecutionContext *context = f->engine()->current; + ExecutionContext *ctx = f->engine()->newCallContext(stackSpace, f, thisObject, args, argc); + + if (!f->strictMode && !thisObject.isObject()) { + if (thisObject.isUndefined() || thisObject.isNull()) { + ctx->thisObject = Value::fromObject(f->engine()->globalObject); + } else { + ctx->thisObject = Value::fromObject(thisObject.toObject(context)); + } + } + + Value result; + try { + result = f->function->code(ctx, f->function->codeData); + } catch (Exception &ex) { + ex.partiallyUnwindContext(context); + throw; + } + ctx->engine->popContext(); + return result; +} + + + +DEFINE_MANAGED_VTABLE(BuiltinFunctionOld); + +BuiltinFunctionOld::BuiltinFunctionOld(ExecutionContext *scope, String *name, Value (*code)(SimpleCallContext *)) + : FunctionObject(scope, name) + , code(code) +{ + vtbl = &static_vtbl; + isBuiltinFunction = true; +} + +Value BuiltinFunctionOld::construct(Managed *f, Value *, int) +{ + f->engine()->current->throwTypeError(); + return Value::undefinedValue(); +} + +Value BuiltinFunctionOld::call(Managed *that, const Value &thisObject, Value *args, int argc) +{ + BuiltinFunctionOld *f = static_cast(that); + ExecutionEngine *v4 = f->engine(); + ExecutionContext *context = v4->current; + + SimpleCallContext ctx; + ctx.initSimpleCallContext(f->scope->engine); + ctx.strictMode = f->scope->strictMode; // ### needed? scope or parent context? + ctx.thisObject = thisObject; + ctx.arguments = args; + ctx.argumentCount = argc; + v4->pushContext(&ctx); + + if (!f->strictMode && !thisObject.isObject()) { + // Built-in functions allow for the this object to be null or undefined. This overrides + // the behaviour of changing thisObject to the global object if null/undefined and allows + // the built-in functions for example to throw a type error if null is passed. + if (!thisObject.isUndefined() && !thisObject.isNull()) + ctx.thisObject = Value::fromObject(thisObject.toObject(context)); + } + + Value result = Value::undefinedValue(); + try { + result = f->code(&ctx); + } catch (Exception &ex) { + ex.partiallyUnwindContext(context); + throw; + } + + context->engine->popContext(); + return result; +} + +Value IndexedBuiltinFunction::call(Managed *that, const Value &thisObject, Value *args, int argc) +{ + IndexedBuiltinFunction *f = static_cast(that); + ExecutionEngine *v4 = f->engine(); + ExecutionContext *context = v4->current; + + SimpleCallContext ctx; + ctx.initSimpleCallContext(f->scope->engine); + ctx.strictMode = f->scope->strictMode; // ### needed? scope or parent context? + ctx.thisObject = thisObject; + ctx.arguments = args; + ctx.argumentCount = argc; + v4->pushContext(&ctx); + + if (!f->strictMode && !thisObject.isObject()) { + // Built-in functions allow for the this object to be null or undefined. This overrides + // the behaviour of changing thisObject to the global object if null/undefined and allows + // the built-in functions for example to throw a type error if null is passed. + if (!thisObject.isUndefined() && !thisObject.isNull()) + ctx.thisObject = Value::fromObject(thisObject.toObject(context)); + } + + Value result = Value::undefinedValue(); + try { + result = f->code(&ctx, f->index); + } catch (Exception &ex) { + ex.partiallyUnwindContext(context); + throw; + } + + context->engine->popContext(); + return result; +} + +DEFINE_MANAGED_VTABLE(IndexedBuiltinFunction); + +DEFINE_MANAGED_VTABLE(BoundFunction); + +BoundFunction::BoundFunction(ExecutionContext *scope, FunctionObject *target, Value boundThis, const QVector &boundArgs) + : FunctionObject(scope, 0) + , target(target) + , boundThis(boundThis) + , boundArgs(boundArgs) +{ + vtbl = &static_vtbl; + int len = target->get(scope->engine->id_length).toUInt32(); + len -= boundArgs.size(); + if (len < 0) + len = 0; + defineReadonlyProperty(scope->engine->id_length, Value::fromInt32(len)); + + FunctionObject *thrower = scope->engine->newBuiltinFunction(scope, 0, throwTypeError); + Property pd = Property::fromAccessor(thrower, thrower); + *insertMember(scope->engine->id_arguments, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable) = pd; + *insertMember(scope->engine->id_caller, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable) = pd; +} + +void BoundFunction::destroy(Managed *that) +{ + static_cast(that)->~BoundFunction(); +} + +Value BoundFunction::call(Managed *that, const Value &, Value *args, int argc) +{ + BoundFunction *f = static_cast(that); + Value *newArgs = static_cast(alloca(sizeof(Value)*(f->boundArgs.size() + argc))); + memcpy(newArgs, f->boundArgs.constData(), f->boundArgs.size()*sizeof(Value)); + memcpy(newArgs + f->boundArgs.size(), args, argc*sizeof(Value)); + + return f->target->call(f->boundThis, newArgs, f->boundArgs.size() + argc); +} + +Value BoundFunction::construct(Managed *that, Value *args, int argc) +{ + BoundFunction *f = static_cast(that); + Value *newArgs = static_cast(alloca(sizeof(Value)*(f->boundArgs.size() + argc))); + memcpy(newArgs, f->boundArgs.constData(), f->boundArgs.size()*sizeof(Value)); + memcpy(newArgs + f->boundArgs.size(), args, argc*sizeof(Value)); + + return f->target->construct(newArgs, f->boundArgs.size() + argc); +} + +bool BoundFunction::hasInstance(Managed *that, const Value &value) +{ + BoundFunction *f = static_cast(that); + return FunctionObject::hasInstance(f->target, value); +} + +void BoundFunction::markObjects(Managed *that) +{ + BoundFunction *o = static_cast(that); + o->target->mark(); + if (Managed *m = o->boundThis.asManaged()) + m->mark(); + for (int i = 0; i < o->boundArgs.size(); ++i) + if (Managed *m = o->boundArgs.at(i).asManaged()) + m->mark(); + FunctionObject::markObjects(that); +} diff --git a/src/qml/jsruntime/qv4functionobject_p.h b/src/qml/jsruntime/qv4functionobject_p.h new file mode 100644 index 0000000000..8465142616 --- /dev/null +++ b/src/qml/jsruntime/qv4functionobject_p.h @@ -0,0 +1,221 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4FUNCTIONOBJECT_H +#define QV4FUNCTIONOBJECT_H + +#include "qv4global_p.h" +#include "qv4runtime_p.h" +#include "qv4engine_p.h" +#include "qv4context_p.h" +#include "qv4object_p.h" +#include "qv4string_p.h" +#include "qv4managed_p.h" +#include "qv4property_p.h" +#include "qv4objectiterator_p.h" + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct Function; +struct Object; +struct BooleanObject; +struct NumberObject; +struct StringObject; +struct ArrayObject; +struct DateObject; +struct FunctionObject; +struct ErrorObject; +struct ArgumentsObject; +struct ExecutionContext; +struct ExecutionEngine; +class MemoryManager; + +struct ObjectPrototype; +struct StringPrototype; +struct NumberPrototype; +struct BooleanPrototype; +struct ArrayPrototype; +struct FunctionPrototype; +struct DatePrototype; +struct ErrorPrototype; +struct EvalErrorPrototype; +struct RangeErrorPrototype; +struct ReferenceErrorPrototype; +struct SyntaxErrorPrototype; +struct TypeErrorPrototype; +struct URIErrorPrototype; +struct InternalClass; +struct Lookup; + +struct Q_QML_EXPORT FunctionObject: Object { + // Used with Managed::subType + enum FunctionType { + RegularFunction = 0, + WrappedQtMethod = 1 + }; + + ExecutionContext *scope; + String *name; + String * const *formalParameterList; + String * const *varList; + unsigned int formalParameterCount; + unsigned int varCount; + Function *function; + + FunctionObject(ExecutionContext *scope, String *name = 0); + + Value newInstance(); + + static Value construct(Managed *that, Value *args, int argc); + static Value call(Managed *that, const Value &, Value *, int); + inline Value construct(Value *args, int argc) { + return vtbl->construct(this, args, argc); + } + inline Value call(const Value &thisObject, Value *args, int argc) { + return vtbl->call(this, thisObject, args, argc); + } + +protected: + static const ManagedVTable static_vtbl; + static void markObjects(Managed *that); + static bool hasInstance(Managed *that, const Value &value); +}; + +struct FunctionCtor: FunctionObject +{ + FunctionCtor(ExecutionContext *scope); + + static Value construct(Managed *that, Value *args, int argc); + static Value call(Managed *that, const Value &, Value *, int); + +protected: + static const ManagedVTable static_vtbl; +}; + +struct FunctionPrototype: FunctionObject +{ + FunctionPrototype(ExecutionContext *ctx); + void init(ExecutionContext *ctx, const Value &ctor); + + static Value method_toString(SimpleCallContext *ctx); + static Value method_apply(SimpleCallContext *ctx); + static Value method_call(SimpleCallContext *ctx); + static Value method_bind(SimpleCallContext *ctx); +}; + +struct BuiltinFunctionOld: FunctionObject { + Value (*code)(SimpleCallContext *); + + BuiltinFunctionOld(ExecutionContext *scope, String *name, Value (*code)(SimpleCallContext *)); + + static Value construct(Managed *, Value *args, int argc); + static Value call(Managed *that, const Value &, Value *, int); + +protected: + static const ManagedVTable static_vtbl; +}; + +struct IndexedBuiltinFunction: FunctionObject +{ + Q_MANAGED + + Value (*code)(SimpleCallContext *ctx, uint index); + uint index; + + IndexedBuiltinFunction(ExecutionContext *scope, uint index, Value (*code)(SimpleCallContext *ctx, uint index)) + : FunctionObject(scope, /*name*/0) + , code(code) + , index(index) + { + vtbl = &static_vtbl; + isBuiltinFunction = true; + } + + static Value construct(Managed *m, Value *, int) + { + m->engine()->current->throwTypeError(); + return Value::undefinedValue(); + } + + static Value call(Managed *that, const Value &thisObject, Value *args, int argc); +}; + + +struct ScriptFunction: FunctionObject { + ScriptFunction(ExecutionContext *scope, Function *function); + + static Value construct(Managed *, Value *args, int argc); + static Value call(Managed *that, const Value &, Value *, int); + +protected: + static const ManagedVTable static_vtbl; +}; + +struct BoundFunction: FunctionObject { + FunctionObject *target; + Value boundThis; + QVector boundArgs; + + BoundFunction(ExecutionContext *scope, FunctionObject *target, Value boundThis, const QVector &boundArgs); + ~BoundFunction() {} + + + static Value construct(Managed *, Value *args, int argc); + static Value call(Managed *that, const Value &, Value *, int); + + static const ManagedVTable static_vtbl; + static void destroy(Managed *); + static void markObjects(Managed *that); + static bool hasInstance(Managed *that, const Value &value); +}; + +} + +QT_END_NAMESPACE + +#endif // QMLJS_OBJECTS_H diff --git a/src/qml/jsruntime/qv4global_p.h b/src/qml/jsruntime/qv4global_p.h new file mode 100644 index 0000000000..8e47f3c88a --- /dev/null +++ b/src/qml/jsruntime/qv4global_p.h @@ -0,0 +1,197 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4GLOBAL_H +#define QV4GLOBAL_H + +#include + +#include + +#if defined(Q_CC_MSVC) +namespace std { + +inline bool isinf(double d) { return !_finite(d) && !_isnan(d); } +inline bool isnan(double d) { return !!_isnan(d); } +inline bool isfinite(double d) { return _finite(d); } +inline bool signbit(double d) { return _copysign(1.0, d) < 0; } + +} // namespace std + +inline double trunc(double d) { return d > 0 ? floor(d) : ceil(d); } +#endif + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +class MemoryManager; +struct String; +struct Object; +struct ObjectPrototype; +struct ObjectIterator; +struct ExecutionContext; +struct ScriptFunction; +struct InternalClass; +struct Property; + +struct BooleanObject; +struct NumberObject; +struct StringObject; +struct ArrayObject; +struct DateObject; +struct FunctionObject; +struct ErrorObject; +struct ArgumentsObject; +struct Managed; +struct Lookup; +struct ExecutionEngine; +struct QObjectWrapper; + + +enum PropertyFlag { + Attr_Data = 0, + Attr_Accessor = 0x1, + Attr_NotWritable = 0x2, + Attr_NotEnumerable = 0x4, + Attr_NotConfigurable = 0x8, + Attr_ReadOnly = Attr_NotWritable|Attr_NotEnumerable|Attr_NotConfigurable, + Attr_Invalid = 0xff +}; + +Q_DECLARE_FLAGS(PropertyFlags, PropertyFlag); +Q_DECLARE_OPERATORS_FOR_FLAGS(PropertyFlags); + +struct PropertyAttributes +{ + union { + uchar m_all; + struct { + uchar m_flags : 4; + uchar m_mask : 4; + }; + struct { + uchar m_type : 1; + uchar m_writable : 1; + uchar m_enumerable : 1; + uchar m_configurable : 1; + uchar type_set : 1; + uchar writable_set : 1; + uchar enumerable_set : 1; + uchar configurable_set : 1; + }; + }; + + enum Type { + Data = 0, + Accessor = 1, + Generic = 2 + }; + + PropertyAttributes() : m_all(0) {} + PropertyAttributes(PropertyFlag f) : m_all(0) { + if (f != Attr_Invalid) { + setType(f & Attr_Accessor ? Accessor : Data); + if (!(f & Attr_Accessor)) + setWritable(!(f & Attr_NotWritable)); + setEnumerable(!(f & Attr_NotEnumerable)); + setConfigurable(!(f & Attr_NotConfigurable)); + } + } + PropertyAttributes(PropertyFlags f) : m_all(0) { + if (f != Attr_Invalid) { + setType(f & Attr_Accessor ? Accessor : Data); + if (!(f & Attr_Accessor)) + setWritable(!(f & Attr_NotWritable)); + setEnumerable(!(f & Attr_NotEnumerable)); + setConfigurable(!(f & Attr_NotConfigurable)); + } + } + PropertyAttributes(const PropertyAttributes &other) : m_all(other.m_all) {} + PropertyAttributes & operator=(const PropertyAttributes &other) { m_all = other.m_all; return *this; } + + void setType(Type t) { m_type = t; type_set = true; } + Type type() const { return type_set ? (Type)m_type : Generic; } + + bool isData() const { return type() == PropertyAttributes::Data || writable_set; } + bool isAccessor() const { return type() == PropertyAttributes::Accessor; } + bool isGeneric() const { return type() == PropertyAttributes::Generic && !writable_set; } + + bool hasType() const { return type_set; } + bool hasWritable() const { return writable_set; } + bool hasConfigurable() const { return configurable_set; } + bool hasEnumerable() const { return enumerable_set; } + + void setWritable(bool b) { m_writable = b; writable_set = true; } + void setConfigurable(bool b) { m_configurable = b; configurable_set = true; } + void setEnumerable(bool b) { m_enumerable = b; enumerable_set = true; } + + void resolve() { m_mask = 0xf; if (m_type == Accessor) { m_writable = false; writable_set = false; } } + + bool isWritable() const { return m_type != Data || m_writable; } + bool isEnumerable() const { return m_enumerable; } + bool isConfigurable() const { return m_configurable; } + + void clearType() { m_type = Data; type_set = false; } + void clearWritable() { m_writable = false; writable_set = false; } + void clearEnumerable() { m_enumerable = false; enumerable_set = false; } + void clearConfigurable() { m_configurable = false; configurable_set = false; } + + void clear() { m_all = 0; } + bool isEmpty() const { return !m_all; } + + uint flags() const { return m_flags; } + + bool operator==(PropertyAttributes other) { + return m_all == other.m_all; + } + bool operator!=(PropertyAttributes other) { + return m_all != other.m_all; + } +}; + +} + +Q_DECLARE_TYPEINFO(QV4::PropertyAttributes, Q_PRIMITIVE_TYPE); + +QT_END_NAMESPACE + +#endif // QV4GLOBAL_H diff --git a/src/qml/jsruntime/qv4globalobject.cpp b/src/qml/jsruntime/qv4globalobject.cpp new file mode 100644 index 0000000000..6b279416a3 --- /dev/null +++ b/src/qml/jsruntime/qv4globalobject.cpp @@ -0,0 +1,652 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4globalobject_p.h" +#include "qv4mm_p.h" +#include "qv4value_p.h" +#include "qv4context_p.h" +#include "qv4function_p.h" +#include "qv4debugging_p.h" +#include "qv4script_p.h" +#include "qv4exception_p.h" + +#include +#include +#include +#include +#include +#include +#include "private/qlocale_tools_p.h" + +#include +#include +#include +#include "qv4alloca_p.h" + +#include + +using namespace QV4; + + +static inline char toHex(char c) +{ + static const char hexnumbers[] = "0123456789ABCDEF"; + return hexnumbers[c & 0xf]; +} + +static int fromHex(QChar ch) +{ + ushort c = ch.unicode(); + if ((c >= '0') && (c <= '9')) + return c - '0'; + if ((c >= 'A') && (c <= 'F')) + return c - 'A' + 10; + if ((c >= 'a') && (c <= 'f')) + return c - 'a' + 10; + return -1; +} + +static QString escape(const QString &input) +{ + QString output; + output.reserve(input.size() * 3); + const int length = input.length(); + for (int i = 0; i < length; ++i) { + ushort uc = input.at(i).unicode(); + if (uc < 0x100) { + if ( (uc > 0x60 && uc < 0x7B) + || (uc > 0x3F && uc < 0x5B) + || (uc > 0x2C && uc < 0x3A) + || (uc == 0x2A) + || (uc == 0x2B) + || (uc == 0x5F)) { + output.append(QChar(uc)); + } else { + output.append('%'); + output.append(QChar(toHex(uc >> 4))); + output.append(QChar(toHex(uc))); + } + } else { + output.append('%'); + output.append('u'); + output.append(QChar(toHex(uc >> 12))); + output.append(QChar(toHex(uc >> 8))); + output.append(QChar(toHex(uc >> 4))); + output.append(QChar(toHex(uc))); + } + } + return output; +} + +static QString unescape(const QString &input) +{ + QString result; + result.reserve(input.length()); + int i = 0; + const int length = input.length(); + while (i < length) { + QChar c = input.at(i++); + if ((c == '%') && (i + 1 < length)) { + QChar a = input.at(i); + if ((a == 'u') && (i + 4 < length)) { + int d3 = fromHex(input.at(i+1)); + int d2 = fromHex(input.at(i+2)); + int d1 = fromHex(input.at(i+3)); + int d0 = fromHex(input.at(i+4)); + if ((d3 != -1) && (d2 != -1) && (d1 != -1) && (d0 != -1)) { + ushort uc = ushort((d3 << 12) | (d2 << 8) | (d1 << 4) | d0); + result.append(QChar(uc)); + i += 5; + } else { + result.append(c); + } + } else { + int d1 = fromHex(a); + int d0 = fromHex(input.at(i+1)); + if ((d1 != -1) && (d0 != -1)) { + c = (d1 << 4) | d0; + i += 2; + } + result.append(c); + } + } else { + result.append(c); + } + } + return result; +} + +static const char uriReserved[] = ";/?:@&=+$,#"; +static const char uriUnescaped[] = "-_.!~*'()"; +static const char uriUnescapedReserved[] = "-_.!~*'();/?:@&=+$,#"; + +static void addEscapeSequence(QString &output, uchar ch) +{ + output.append(QLatin1Char('%')); + output.append(QLatin1Char(toHex(ch >> 4))); + output.append(QLatin1Char(toHex(ch & 0xf))); +} + +static QString encode(const QString &input, const char *unescapedSet, bool *ok) +{ + *ok = true; + QString output; + const int length = input.length(); + int i = 0; + while (i < length) { + const QChar c = input.at(i); + bool escape = true; + if ((c.unicode() >= 'a' && c.unicode() <= 'z') || + (c.unicode() >= 'A' && c.unicode() <= 'Z') || + (c.unicode() >= '0' && c.unicode() <= '9')) { + escape = false; + } else { + const char *r = unescapedSet; + while (*r) { + if (*r == c.unicode()) { + escape = false; + break; + } + ++r; + } + } + if (escape) { + uint uc = c.unicode(); + if ((uc >= 0xDC00) && (uc <= 0xDFFF)) { + *ok = false; + break; + } + if (!((uc < 0xD800) || (uc > 0xDBFF))) { + ++i; + if (i == length) { + *ok = false; + break; + } + const uint uc2 = input.at(i).unicode(); + if ((uc2 < 0xDC00) || (uc2 > 0xDFFF)) { + *ok = false; + break; + } + uc = ((uc - 0xD800) * 0x400) + (uc2 - 0xDC00) + 0x10000; + } + if (uc < 0x80) { + addEscapeSequence(output, (uchar)uc); + } else { + if (uc < 0x0800) { + addEscapeSequence(output, 0xc0 | ((uchar) (uc >> 6))); + } else { + + if (QChar::requiresSurrogates(uc)) { + addEscapeSequence(output, 0xf0 | ((uchar) (uc >> 18))); + addEscapeSequence(output, 0x80 | (((uchar) (uc >> 12)) & 0x3f)); + } else { + addEscapeSequence(output, 0xe0 | (((uchar) (uc >> 12)) & 0x3f)); + } + addEscapeSequence(output, 0x80 | (((uchar) (uc >> 6)) & 0x3f)); + } + addEscapeSequence(output, 0x80 | ((uchar) (uc&0x3f))); + } + } else { + output.append(c); + } + ++i; + } + if (i != length) + *ok = false; + return output; +} + +enum DecodeMode { + DecodeAll, + DecodeNonReserved +}; + +static QString decode(const QString &input, DecodeMode decodeMode, bool *ok) +{ + *ok = true; + QString output; + output.reserve(input.length()); + const int length = input.length(); + int i = 0; + const QChar percent = QLatin1Char('%'); + while (i < length) { + const QChar ch = input.at(i); + if (ch == percent) { + int start = i; + if (i + 2 >= length) + goto error; + + int d1 = fromHex(input.at(i+1)); + int d0 = fromHex(input.at(i+2)); + if ((d1 == -1) || (d0 == -1)) + goto error; + + int b = (d1 << 4) | d0; + i += 2; + if (b & 0x80) { + int uc; + int min_uc; + int need; + if ((b & 0xe0) == 0xc0) { + uc = b & 0x1f; + need = 1; + min_uc = 0x80; + } else if ((b & 0xf0) == 0xe0) { + uc = b & 0x0f; + need = 2; + min_uc = 0x800; + } else if ((b & 0xf8) == 0xf0) { + uc = b & 0x07; + need = 3; + min_uc = 0x10000; + } else { + goto error; + } + + if (i + (3 * need) >= length) + goto error; + + for (int j = 0; j < need; ++j) { + ++i; + if (input.at(i) != percent) + goto error; + + d1 = fromHex(input.at(i+1)); + d0 = fromHex(input.at(i+2)); + if ((d1 == -1) || (d0 == -1)) + goto error; + + b = (d1 << 4) | d0; + if ((b & 0xC0) != 0x80) + goto error; + + i += 2; + uc = (uc << 6) + (b & 0x3f); + } + if (uc < min_uc) + goto error; + + if (uc < 0x10000) { + output.append(QChar(uc)); + } else { + if (uc > 0x10FFFF) + goto error; + + ushort l = ushort(((uc - 0x10000) & 0x3FF) + 0xDC00); + ushort h = ushort((((uc - 0x10000) >> 10) & 0x3FF) + 0xD800); + output.append(QChar(h)); + output.append(QChar(l)); + } + } else { + if (decodeMode == DecodeNonReserved && b <= 0x40) { + const char *r = uriReserved; + while (*r) { + if (*r == b) + break; + ++r; + } + if (*r) + output.append(input.mid(start, i - start + 1)); + else + output.append(QChar(b)); + } else { + output.append(QChar(b)); + } + } + } else { + output.append(ch); + } + ++i; + } + if (i != length) + *ok = false; + return output; + error: + *ok = false; + return QString(); +} + +DEFINE_MANAGED_VTABLE(EvalFunction); + +EvalFunction::EvalFunction(ExecutionContext *scope) + : FunctionObject(scope, scope->engine->id_eval) +{ + vtbl = &static_vtbl; + defineReadonlyProperty(scope->engine->id_length, Value::fromInt32(1)); +} + +Value EvalFunction::evalCall(Value /*thisObject*/, Value *args, int argc, bool directCall) +{ + if (argc < 1) + return Value::undefinedValue(); + + ExecutionContext *parentContext = engine()->current; + ExecutionEngine *engine = parentContext->engine; + ExecutionContext *ctx = parentContext; + + if (!directCall) { + // the context for eval should be the global scope, so we fake a root + // context + ctx = engine->pushGlobalContext(); + } + + if (!args[0].isString()) + return args[0]; + + const QString code = args[0].stringValue()->toQString(); + bool inheritContext = !ctx->strictMode; + + Script script(ctx, code, QString("eval code")); + script.strictMode = (directCall && parentContext->strictMode); + script.inheritContext = inheritContext; + script.parse(); + + Function *function = script.function(); + if (!function) + return Value::undefinedValue(); + + strictMode = function->isStrict || (ctx->strictMode); + + usesArgumentsObject = function->usesArgumentsObject; + needsActivation = function->needsActivation(); + + if (strictMode) { + FunctionObject *e = engine->newScriptFunction(ctx, function); + return e->call(ctx->thisObject, 0, 0); + } + + ExecutionContext::EvalCode evalCode; + evalCode.function = function; + evalCode.next = ctx->currentEvalCode; + ctx->currentEvalCode = &evalCode; + + // set the correct strict mode flag on the context + bool cstrict = ctx->strictMode; + ctx->strictMode = strictMode; + + Value result = Value::undefinedValue(); + try { + result = function->code(ctx, function->codeData); + } catch (Exception &ex) { + ctx->strictMode = cstrict; + ctx->currentEvalCode = evalCode.next; + if (strictMode) + ex.partiallyUnwindContext(parentContext); + throw; + } + + ctx->strictMode = cstrict; + ctx->currentEvalCode = evalCode.next; + + while (engine->current != parentContext) + engine->popContext(); + + return result; +} + + +Value EvalFunction::call(Managed *that, const Value &thisObject, Value *args, int argc) +{ + // indirect call + return static_cast(that)->evalCall(thisObject, args, argc, false); +} + + +static inline int toInt(const QChar &qc, int R) +{ + ushort c = qc.unicode(); + int v = -1; + if (c >= '0' && c <= '9') + v = c - '0'; + else if (c >= 'A' && c <= 'Z') + v = c - 'A' + 10; + else if (c >= 'a' && c <= 'z') + v = c - 'a' + 10; + if (v >= 0 && v < R) + return v; + else + return -1; +} + +// parseInt [15.1.2.2] +Value GlobalFunctions::method_parseInt(SimpleCallContext *context) +{ + Value string = context->argument(0); + Value radix = context->argument(1); + int R = radix.isUndefined() ? 0 : radix.toInt32(); + + // [15.1.2.2] step by step: + String *inputString = string.toString(context); // 1 + QString trimmed = inputString->toQString().trimmed(); // 2 + const QChar *pos = trimmed.constData(); + const QChar *end = pos + trimmed.length(); + + int sign = 1; // 3 + if (pos != end) { + if (*pos == QLatin1Char('-')) + sign = -1; // 4 + if (*pos == QLatin1Char('-') || *pos == QLatin1Char('+')) + ++pos; // 5 + } + bool stripPrefix = true; // 7 + if (R) { // 8 + if (R < 2 || R > 36) + return Value::fromDouble(std::numeric_limits::quiet_NaN()); // 8a + if (R != 16) + stripPrefix = false; // 8b + } else { // 9 + R = 10; // 9a + } + if (stripPrefix) { // 10 + if ((end - pos >= 2) + && (pos[0] == QLatin1Char('0')) + && (pos[1] == QLatin1Char('x') || pos[1] == QLatin1Char('X'))) { // 10a + pos += 2; + R = 16; + } + } + // 11: Z is progressively built below + // 13: this is handled by the toInt function + if (pos == end) // 12 + return Value::fromDouble(std::numeric_limits::quiet_NaN()); + bool overflow = false; + qint64 v_overflow; + unsigned overflow_digit_count = 0; + int d = toInt(*pos++, R); + if (d == -1) + return Value::fromDouble(std::numeric_limits::quiet_NaN()); + qint64 v = d; + while (pos != end) { + d = toInt(*pos++, R); + if (d == -1) + break; + if (overflow) { + if (overflow_digit_count == 0) { + v_overflow = v; + v = 0; + } + ++overflow_digit_count; + v = v * R + d; + } else { + qint64 vNew = v * R + d; + if (vNew < v) { + overflow = true; + --pos; + } else { + v = vNew; + } + } + } + + if (overflow) { + double result = (double) v_overflow * pow(R, overflow_digit_count); + result += v; + return Value::fromDouble(sign * result); + } else { + return Value::fromDouble(sign * (double) v); // 15 + } +} + +// parseFloat [15.1.2.3] +Value GlobalFunctions::method_parseFloat(SimpleCallContext *context) +{ + Value string = context->argument(0); + + // [15.1.2.3] step by step: + String *inputString = string.toString(context); // 1 + QString trimmed = inputString->toQString().trimmed(); // 2 + + // 4: + if (trimmed.startsWith(QLatin1String("Infinity")) + || trimmed.startsWith(QLatin1String("+Infinity"))) + return Value::fromDouble(Q_INFINITY); + if (trimmed.startsWith("-Infinity")) + return Value::fromDouble(-Q_INFINITY); + QByteArray ba = trimmed.toLatin1(); + bool ok; + const char *begin = ba.constData(); + const char *end = 0; + double d = qstrtod(begin, &end, &ok); + if (end - begin == 0) + return Value::fromDouble(std::numeric_limits::quiet_NaN()); // 3 + else + return Value::fromDouble(d); +} + +/// isNaN [15.1.2.4] +Value GlobalFunctions::method_isNaN(SimpleCallContext *context) +{ + const Value &v = context->argument(0); + if (v.integerCompatible()) + return Value::fromBoolean(false); + + double d = v.toNumber(); + return Value::fromBoolean(std::isnan(d)); +} + +/// isFinite [15.1.2.5] +Value GlobalFunctions::method_isFinite(SimpleCallContext *context) +{ + const Value &v = context->argument(0); + if (v.integerCompatible()) + return Value::fromBoolean(true); + + double d = v.toNumber(); + return Value::fromBoolean(std::isfinite(d)); +} + +/// decodeURI [15.1.3.1] +Value GlobalFunctions::method_decodeURI(SimpleCallContext *context) +{ + if (context->argumentCount == 0) + return Value::undefinedValue(); + + QString uriString = context->arguments[0].toString(context)->toQString(); + bool ok; + QString out = decode(uriString, DecodeNonReserved, &ok); + if (!ok) + context->throwURIError(Value::fromString(context, QStringLiteral("malformed URI sequence"))); + + return Value::fromString(context, out); +} + +/// decodeURIComponent [15.1.3.2] +Value GlobalFunctions::method_decodeURIComponent(SimpleCallContext *context) +{ + if (context->argumentCount == 0) + return Value::undefinedValue(); + + QString uriString = context->arguments[0].toString(context)->toQString(); + bool ok; + QString out = decode(uriString, DecodeAll, &ok); + if (!ok) + context->throwURIError(Value::fromString(context, QStringLiteral("malformed URI sequence"))); + + return Value::fromString(context, out); +} + +/// encodeURI [15.1.3.3] +Value GlobalFunctions::method_encodeURI(SimpleCallContext *context) +{ + if (context->argumentCount == 0) + return Value::undefinedValue(); + + QString uriString = context->arguments[0].toString(context)->toQString(); + bool ok; + QString out = encode(uriString, uriUnescapedReserved, &ok); + if (!ok) + context->throwURIError(Value::fromString(context, QStringLiteral("malformed URI sequence"))); + + return Value::fromString(context, out); +} + +/// encodeURIComponent [15.1.3.4] +Value GlobalFunctions::method_encodeURIComponent(SimpleCallContext *context) +{ + if (context->argumentCount == 0) + return Value::undefinedValue(); + + QString uriString = context->arguments[0].toString(context)->toQString(); + bool ok; + QString out = encode(uriString, uriUnescaped, &ok); + if (!ok) + context->throwURIError(Value::fromString(context, QStringLiteral("malformed URI sequence"))); + + return Value::fromString(context, out); +} + +Value GlobalFunctions::method_escape(SimpleCallContext *context) +{ + if (!context->argumentCount) + return Value::fromString(context, QStringLiteral("undefined")); + + QString str = context->argument(0).toString(context)->toQString(); + return Value::fromString(context, escape(str)); +} + +Value GlobalFunctions::method_unescape(SimpleCallContext *context) +{ + if (!context->argumentCount) + return Value::fromString(context, QStringLiteral("undefined")); + + QString str = context->argument(0).toString(context)->toQString(); + return Value::fromString(context, unescape(str)); +} diff --git a/src/qml/jsruntime/qv4globalobject_p.h b/src/qml/jsruntime/qv4globalobject_p.h new file mode 100644 index 0000000000..11d034b5b4 --- /dev/null +++ b/src/qml/jsruntime/qv4globalobject_p.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4GLOBALOBJECT_H +#define QV4GLOBALOBJECT_H + +#include "qv4global_p.h" +#include "qv4functionobject_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct Q_QML_EXPORT EvalFunction : FunctionObject +{ + EvalFunction(ExecutionContext *scope); + + Value evalCall(Value thisObject, Value *args, int argc, bool directCall); + + using Managed::construct; + static Value call(Managed *that, const Value &, Value *, int); + +protected: + static const ManagedVTable static_vtbl; +}; + +struct GlobalFunctions +{ + static Value method_parseInt(SimpleCallContext *context); + static Value method_parseFloat(SimpleCallContext *context); + static Value method_isNaN(SimpleCallContext *context); + static Value method_isFinite(SimpleCallContext *context); + static Value method_decodeURI(SimpleCallContext *context); + static Value method_decodeURIComponent(SimpleCallContext *context); + static Value method_encodeURI(SimpleCallContext *context); + static Value method_encodeURIComponent(SimpleCallContext *context); + static Value method_escape(SimpleCallContext *context); + static Value method_unescape(SimpleCallContext *context); +}; + +} + +QT_END_NAMESPACE + +#endif // QMLJS_OBJECTS_H diff --git a/src/qml/jsruntime/qv4identifier.cpp b/src/qml/jsruntime/qv4identifier.cpp new file mode 100644 index 0000000000..5d8077bfdc --- /dev/null +++ b/src/qml/jsruntime/qv4identifier.cpp @@ -0,0 +1,172 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "qv4identifier_p.h" +#include "qv4identifiertable_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +static const uchar prime_deltas[] = { + 0, 0, 1, 3, 1, 5, 3, 3, 1, 9, 7, 5, 3, 9, 25, 3, + 1, 21, 3, 21, 7, 15, 9, 5, 3, 29, 15, 0, 0, 0, 0, 0 +}; + +static inline int primeForNumBits(int numBits) +{ + return (1 << numBits) + prime_deltas[numBits]; +} + + +IdentifierHashData::IdentifierHashData(int numBits) + : numBits(numBits) + , size(0) +{ + refCount.store(1); + alloc = primeForNumBits(numBits); + entries = (IdentifierHashEntry *)malloc(alloc*sizeof(IdentifierHashEntry)); + memset(entries, 0, alloc*sizeof(IdentifierHashEntry)); +} + +IdentifierHashBase::IdentifierHashBase(ExecutionEngine *engine) +{ + d = new IdentifierHashData(3); + d->identifierTable = engine->identifierTable; +} + + +IdentifierHashEntry *IdentifierHashBase::addEntry(const Identifier *identifier) +{ + // fill up to max 50% + bool grow = (d->alloc <= d->size*2); + + if (grow) { + ++d->numBits; + int newAlloc = primeForNumBits(d->numBits); + IdentifierHashEntry *newEntries = (IdentifierHashEntry *)malloc(newAlloc * sizeof(IdentifierHashEntry)); + memset(newEntries, 0, newAlloc*sizeof(IdentifierHashEntry)); + for (uint i = 0; i < d->alloc; ++i) { + const IdentifierHashEntry &e = d->entries[i]; + if (!e.identifier) + continue; + uint idx = e.identifier->hashValue % newAlloc; + while (newEntries[idx].identifier) { + ++idx; + idx %= newAlloc; + } + newEntries[idx] = e; + } + free(d->entries); + d->entries = newEntries; + d->alloc = newAlloc; + } + + uint idx = identifier->hashValue % d->alloc; + while (d->entries[idx].identifier) { + Q_ASSERT(d->entries[idx].identifier != identifier); + ++idx; + idx %= d->alloc; + } + d->entries[idx].identifier = identifier; + ++d->size; + return d->entries + idx; +} + +const IdentifierHashEntry *IdentifierHashBase::lookup(const Identifier *identifier) const +{ + if (!d) + return 0; + assert(d->entries); + + uint idx = identifier->hashValue % d->alloc; + while (1) { + if (!d->entries[idx].identifier) + return 0; + if (d->entries[idx].identifier == identifier) + return d->entries + idx; + ++idx; + idx %= d->alloc; + } +} + +const IdentifierHashEntry *IdentifierHashBase::lookup(const QString &str) const +{ + if (!d) + return 0; + assert(d->entries); + + uint hash = String::createHashValue(str.constData(), str.length()); + uint idx = hash % d->alloc; + while (1) { + if (!d->entries[idx].identifier) + return 0; + if (d->entries[idx].identifier->string == str) + return d->entries + idx; + ++idx; + idx %= d->alloc; + } +} + +const IdentifierHashEntry *IdentifierHashBase::lookup(String *str) const +{ + if (!d) + return 0; + if (str->identifier) + return lookup(str->identifier); + return lookup(str->toQString()); +} + +const Identifier *IdentifierHashBase::toIdentifier(const QString &str) const +{ + Q_ASSERT(d); + return d->identifierTable->identifier(str); +} + +const Identifier *IdentifierHashBase::toIdentifier(String *str) const +{ + Q_ASSERT(d); + return d->identifierTable->identifier(str); +} + + +} + +QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4identifier_p.h b/src/qml/jsruntime/qv4identifier_p.h new file mode 100644 index 0000000000..7c69e1d8c4 --- /dev/null +++ b/src/qml/jsruntime/qv4identifier_p.h @@ -0,0 +1,220 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4IDENTIFIER_H +#define QV4IDENTIFIER_H + +#include + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct String; +struct IdentifierTable; +struct ExecutionEngine; + +struct Identifier +{ + QString string; + uint hashValue; +}; + + +struct IdentifierHashEntry { + const Identifier *identifier; + union { + int value; + void *pointer; + }; + int get(int *) const { return this ? value : -1; } + bool get(bool *) const { return this != 0; } + void *get(void **) const { return this ? pointer : 0; } +}; + +struct IdentifierHashData +{ + IdentifierHashData(int numBits); + ~IdentifierHashData() { + free(entries); + } + + QBasicAtomicInt refCount; + int alloc; + int size; + int numBits; + IdentifierTable *identifierTable; + IdentifierHashEntry *entries; +}; + +struct IdentifierHashBase +{ + + IdentifierHashData *d; + + IdentifierHashBase() : d(0) {} + IdentifierHashBase(ExecutionEngine *engine); + inline IdentifierHashBase(const IdentifierHashBase &other); + inline ~IdentifierHashBase(); + inline IdentifierHashBase &operator=(const IdentifierHashBase &other); + + bool isEmpty() const { return !d; } + // ### + void reserve(int) {} + + inline int count() const; + bool contains(const Identifier *i) const; + bool contains(const QString &str) const; + bool contains(String *str) const; + +protected: + IdentifierHashEntry *addEntry(const Identifier *i); + const IdentifierHashEntry *lookup(const Identifier *identifier) const; + const IdentifierHashEntry *lookup(const QString &str) const; + const IdentifierHashEntry *lookup(String *str) const; + const Identifier *toIdentifier(const QString &str) const; + const Identifier *toIdentifier(String *str) const; +}; + + +template +struct IdentifierHash : public IdentifierHashBase +{ + IdentifierHash() + : IdentifierHashBase() {} + IdentifierHash(ExecutionEngine *engine) + : IdentifierHashBase(engine) {} + inline IdentifierHash(const IdentifierHash &other) + : IdentifierHashBase(other) {} + inline ~IdentifierHash() {} + inline IdentifierHash &operator=(const IdentifierHash &other) { + IdentifierHashBase::operator =(other); + return *this; + } + + void add(const QString &str, const T &value); + + inline T value(const QString &str) const; + inline T value(String *str) const; + QString findId(T value) const; +}; + +inline IdentifierHashBase::IdentifierHashBase(const IdentifierHashBase &other) +{ + d = other.d; + if (d) + d->refCount.ref(); +} + +inline IdentifierHashBase::~IdentifierHashBase() +{ + if (d && !d->refCount.deref()) + delete d; +} + +IdentifierHashBase &IdentifierHashBase::operator=(const IdentifierHashBase &other) +{ + if (other.d) + other.d->refCount.ref(); + if (d && !d->refCount.deref()) + delete d; + d = other.d; + return *this; +} + +inline int IdentifierHashBase::count() const +{ + return d ? d->size : 0; +} + +inline bool IdentifierHashBase::contains(const Identifier *i) const +{ + return lookup(i) != 0; +} + +inline bool IdentifierHashBase::contains(const QString &str) const +{ + return lookup(str) != 0; +} + +inline bool IdentifierHashBase::contains(String *str) const +{ + return lookup(str) != 0; +} + +template +void IdentifierHash::add(const QString &str, const T &value) +{ + IdentifierHashEntry *e = addEntry(toIdentifier(str)); + e->value = value; +} + +template +inline T IdentifierHash::value(const QString &str) const +{ + return lookup(str)->get((T*)0); +} + +template +inline T IdentifierHash::value(String *str) const +{ + return lookup(str)->get((T*)0); +} + + +template +QString IdentifierHash::findId(T value) const +{ + IdentifierHashEntry *e = d->entries; + IdentifierHashEntry *end = e + d->alloc; + while (e < end) { + if (e->identifier && e->get((T*)0) == value) + return e->identifier->string; + ++e; + } + return QString(); +} + + +} + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/jsruntime/qv4identifiertable.cpp b/src/qml/jsruntime/qv4identifiertable.cpp new file mode 100644 index 0000000000..5de2f893ef --- /dev/null +++ b/src/qml/jsruntime/qv4identifiertable.cpp @@ -0,0 +1,184 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "qv4identifiertable_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +static const uchar prime_deltas[] = { + 0, 0, 1, 3, 1, 5, 3, 3, 1, 9, 7, 5, 3, 9, 25, 3, + 1, 21, 3, 21, 7, 15, 9, 5, 3, 29, 15, 0, 0, 0, 0, 0 +}; + +static inline int primeForNumBits(int numBits) +{ + return (1 << numBits) + prime_deltas[numBits]; +} + + +IdentifierTable::IdentifierTable(ExecutionEngine *engine) + : engine(engine) + , size(0) + , numBits(8) +{ + alloc = primeForNumBits(numBits); + entries = (String **)malloc(alloc*sizeof(String *)); + memset(entries, 0, alloc*sizeof(String *)); +} + +IdentifierTable::~IdentifierTable() +{ + free(entries); +} + +void IdentifierTable::addEntry(String *str) +{ + uint hash = str->hashValue(); + + if (str->subtype == String::StringType_ArrayIndex) + return; + + str->identifier = new Identifier; + str->identifier->string = str->toQString(); + str->identifier->hashValue = hash; + + bool grow = (alloc <= size*2); + + if (grow) { + ++numBits; + int newAlloc = primeForNumBits(numBits); + String **newEntries = (String **)malloc(newAlloc*sizeof(String *)); + memset(newEntries, 0, newAlloc*sizeof(String *)); + for (uint i = 0; i < alloc; ++i) { + String *e = entries[i]; + if (!e) + continue; + uint idx = e->stringHash % newAlloc; + while (newEntries[idx]) { + ++idx; + idx %= newAlloc; + } + newEntries[idx] = e; + } + free(entries); + entries = newEntries; + alloc = newAlloc; + } + + uint idx = hash % alloc; + while (entries[idx]) { + ++idx; + idx %= alloc; + } + entries[idx] = str; + ++size; +} + + + +String *IdentifierTable::insertString(const QString &s) +{ + uint hash = String::createHashValue(s.constData(), s.length()); + uint idx = hash % alloc; + while (String *e = entries[idx]) { + if (e->stringHash == hash && e->toQString() == s) + return e; + ++idx; + idx %= alloc; + } + + String *str = engine->newString(s); + addEntry(str); + return str; +} + + +Identifier *IdentifierTable::identifier(String *str) +{ + if (str->identifier) + return str->identifier; + uint hash = str->hashValue(); + if (str->subtype == String::StringType_ArrayIndex) + return 0; + + uint idx = hash % alloc; + while (String *e = entries[idx]) { + if (e->stringHash == hash && e->isEqualTo(str)) { + str->identifier = e->identifier; + return e->identifier; + } + ++idx; + idx %= alloc; + } + + addEntry(str); + return str->identifier; +} + +Identifier *IdentifierTable::identifier(const QString &s) +{ + return insertString(s)->identifier; +} + +Identifier *IdentifierTable::identifier(const char *s, int len) +{ + uint hash = String::createHashValue(s, len); + if (hash == UINT_MAX) + return identifier(QString::fromUtf8(s, len)); + + QLatin1String latin(s, len); + uint idx = hash % alloc; + while (String *e = entries[idx]) { + if (e->stringHash == hash && e->toQString() == latin) + return e->identifier; + ++idx; + idx %= alloc; + } + + String *str = engine->newString(QString::fromLatin1(s, len)); + addEntry(str); + return str->identifier; +} + +} + +QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4identifiertable_p.h b/src/qml/jsruntime/qv4identifiertable_p.h new file mode 100644 index 0000000000..0f9a5921f9 --- /dev/null +++ b/src/qml/jsruntime/qv4identifiertable_p.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4IDENTIFIERTABLE_H +#define QV4IDENTIFIERTABLE_H + +#include "qv4identifier_p.h" +#include "qv4string_p.h" +#include "qv4engine_p.h" +#include + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct IdentifierTable +{ + ExecutionEngine *engine; + + int alloc; + int size; + int numBits; + String **entries; + + void addEntry(String *str); + +public: + + IdentifierTable(ExecutionEngine *engine); + ~IdentifierTable(); + + String *insertString(const QString &s); + + Identifier *identifier(String *str); + Identifier *identifier(const QString &s); + Identifier *identifier(const char *s, int len); + + void mark() { + for (int i = 0; i < alloc; ++i) + if (entries[i]) + entries[i]->mark(); + } +}; + +} + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/jsruntime/qv4include.cpp b/src/qml/jsruntime/qv4include.cpp new file mode 100644 index 0000000000..4fd7bb14c7 --- /dev/null +++ b/src/qml/jsruntime/qv4include.cpp @@ -0,0 +1,232 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4include_p.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +QV4Include::QV4Include(const QUrl &url, QV8Engine *engine, QQmlContextData *context, + const QV4::Value &qmlglobal, const QV4::Value &callback) + : v4(QV8Engine::getV4(engine)), m_network(0), m_reply(0), m_url(url), m_redirectCount(0), m_context(context) +{ + m_qmlglobal = qmlglobal; + if (callback.asFunctionObject()) + m_callbackFunction = callback; + + m_resultObject = resultValue(v4); + + m_network = engine->networkAccessManager(); + + QNetworkRequest request; + request.setUrl(url); + + m_reply = m_network->get(request); + QObject::connect(m_reply, SIGNAL(finished()), this, SLOT(finished())); +} + +QV4Include::~QV4Include() +{ + delete m_reply; m_reply = 0; +} + +QV4::Value QV4Include::resultValue(QV4::ExecutionEngine *v4, Status status) +{ + + // XXX It seems inefficient to create this object from scratch each time. + QV4::Object *o = v4->newObject(); + o->put(v4->newString("OK"), QV4::Value::fromInt32(Ok)); + o->put(v4->newString("LOADING"), QV4::Value::fromInt32(Loading)); + o->put(v4->newString("NETWORK_ERROR"), QV4::Value::fromInt32(NetworkError)); + o->put(v4->newString("EXCEPTION"), QV4::Value::fromInt32(Exception)); + + o->put(v4->newString("status"), QV4::Value::fromInt32(status)); + + return QV4::Value::fromObject(o); +} + +void QV4Include::callback(const QV4::Value &callback, const QV4::Value &status) +{ + QV4::FunctionObject *f = callback.asFunctionObject(); + if (!f) + return; + + QV4::Value args[] = { status }; + QV4::ExecutionContext *ctx = f->engine()->current; + try { + f->call(QV4::Value::fromObject(f->engine()->globalObject), args, 1); + } catch (QV4::Exception &e) { + e.accept(ctx); + } +} + +QV4::Value QV4Include::result() +{ + return m_resultObject.value(); +} + +#define INCLUDE_MAXIMUM_REDIRECT_RECURSION 15 +void QV4Include::finished() +{ + m_redirectCount++; + + if (m_redirectCount < INCLUDE_MAXIMUM_REDIRECT_RECURSION) { + QVariant redirect = m_reply->attribute(QNetworkRequest::RedirectionTargetAttribute); + if (redirect.isValid()) { + m_url = m_url.resolved(redirect.toUrl()); + delete m_reply; + + QNetworkRequest request; + request.setUrl(m_url); + + m_reply = m_network->get(request); + QObject::connect(m_reply, SIGNAL(finished()), this, SLOT(finished())); + return; + } + } + + if (m_reply->error() == QNetworkReply::NoError) { + QByteArray data = m_reply->readAll(); + + QString code = QString::fromUtf8(data); + QQmlScript::Parser::extractPragmas(code); + + QV4::Script script(v4, m_qmlglobal.value().asObject(), code, m_url.toString()); + + QV4::ExecutionContext *ctx = v4->current; + QV4::Object *o = m_resultObject.value().asObject(); + try { + script.parse(); + script.run(); + o->put(v4->newString("status"), QV4::Value::fromInt32(Ok)); + } catch (QV4::Exception &e) { + e.accept(ctx); + o->put(v4->newString("status"), QV4::Value::fromInt32(Exception)); + o->put(v4->newString("exception"), e.value()); + } + } else { + m_resultObject.value().asObject()->put(v4->newString("status"), QV4::Value::fromInt32(NetworkError)); + } + + callback(m_callbackFunction.value(), m_resultObject.value()); + + disconnect(); + deleteLater(); +} + +/* + Documented in qv8engine.cpp +*/ +QV4::Value QV4Include::include(QV4::SimpleCallContext *ctx) +{ + if (!ctx->argumentCount) + return QV4::Value::undefinedValue(); + + QV4::ExecutionEngine *v4 = ctx->engine; + QV8Engine *engine = v4->v8Engine; + QQmlContextData *context = QV4::QmlContextWrapper::callingContext(v4); + + if (!context || !context->isJSContext) + V4THROW_ERROR("Qt.include(): Can only be called from JavaScript files"); + + QUrl url(ctx->engine->resolvedUrl(ctx->arguments[0].toQString())); + + QV4::Value callbackFunction = QV4::Value::undefinedValue(); + if (ctx->argumentCount >= 2 && ctx->arguments[1].asFunctionObject()) + callbackFunction = ctx->arguments[1]; + + QString localFile = QQmlFile::urlToLocalFileOrQrc(url); + + QV4::Value result = QV4::Value::undefinedValue(); + + if (localFile.isEmpty()) { + + QV4Include *i = new QV4Include(url, engine, context, + QV4::Value::fromObject(v4->qmlContextObject()), + callbackFunction); + result = i->result(); + + } else { + + QFile f(localFile); + + if (f.open(QIODevice::ReadOnly)) { + QByteArray data = f.readAll(); + QString code = QString::fromUtf8(data); + QQmlScript::Parser::extractPragmas(code); + + QV4::Object *qmlglobal = v4->qmlContextObject(); + QV4::Script script(v4, qmlglobal, code, url.toString()); + + QV4::ExecutionContext *ctx = v4->current; + try { + script.parse(); + script.run(); + result = resultValue(v4, Ok); + } catch (QV4::Exception &e) { + e.accept(ctx); + result = resultValue(v4, Exception); + result.asObject()->put(v4->newString("exception"), e.value()); + } + } else { + result = resultValue(v4, NetworkError); + } + + callback(callbackFunction, result); + } + + return result; +} + +QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4include_p.h b/src/qml/jsruntime/qv4include_p.h new file mode 100644 index 0000000000..d6bbcd1a60 --- /dev/null +++ b/src/qml/jsruntime/qv4include_p.h @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4INCLUDE_P_H +#define QV4INCLUDE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +class QQmlEngine; +class QNetworkAccessManager; +class QNetworkReply; +class QV8Engine; +class QV4Include : public QObject +{ + Q_OBJECT +public: + enum Status { + Ok = 0, + Loading = 1, + NetworkError = 2, + Exception = 3 + }; + + static QV4::Value include(QV4::SimpleCallContext *ctx); + +private slots: + void finished(); + +private: + QV4Include(const QUrl &url, QV8Engine *engine, QQmlContextData *context, + const QV4::Value &qmlglobal, const QV4::Value &callback); + ~QV4Include(); + + QV4::Value result(); + + static QV4::Value resultValue(QV4::ExecutionEngine *v4, Status status = Loading); + static void callback(const QV4::Value &callback, const QV4::Value &status); + + QV4::ExecutionEngine *v4; + QNetworkAccessManager *m_network; + QPointer m_reply; + + QUrl m_url; + int m_redirectCount; + + QV4::PersistentValue m_callbackFunction; + QV4::PersistentValue m_resultObject; + + QQmlGuardedContextData m_context; + QV4::PersistentValue m_qmlglobal; +}; + +QT_END_NAMESPACE + +#endif + diff --git a/src/qml/jsruntime/qv4internalclass.cpp b/src/qml/jsruntime/qv4internalclass.cpp new file mode 100644 index 0000000000..f4edc99545 --- /dev/null +++ b/src/qml/jsruntime/qv4internalclass.cpp @@ -0,0 +1,296 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include +#include "qv4object_p.h" +#include "qv4identifiertable_p.h" + +QT_BEGIN_NAMESPACE + +uint QV4::qHash(const QV4::InternalClassTransition &t, uint) +{ + return t.id->hashValue ^ t.flags; +} + +using namespace QV4; + +static const uchar prime_deltas[] = { + 0, 0, 1, 3, 1, 5, 3, 3, 1, 9, 7, 5, 3, 9, 25, 3, + 1, 21, 3, 21, 7, 15, 9, 5, 3, 29, 15, 0, 0, 0, 0, 0 +}; + +static inline int primeForNumBits(int numBits) +{ + return (1 << numBits) + prime_deltas[numBits]; +} + +PropertyHashData::PropertyHashData(int numBits) + : numBits(numBits) + , size(0) +{ + refCount.store(1); + alloc = primeForNumBits(numBits); + entries = (PropertyHash::Entry *)malloc(alloc*sizeof(PropertyHash::Entry)); + memset(entries, 0, alloc*sizeof(PropertyHash::Entry)); +} + +void PropertyHash::addEntry(const PropertyHash::Entry &entry, int classSize) +{ + // fill up to max 50% + bool grow = (d->alloc <= d->size*2); + + if (classSize < d->size || grow) { + PropertyHashData *dd = new PropertyHashData(grow ? d->numBits + 1 : d->numBits); + for (uint i = 0; i < d->alloc; ++i) { + const Entry &e = d->entries[i]; + if (!e.identifier || e.index >= classSize) + continue; + uint idx = e.identifier->hashValue % dd->alloc; + while (dd->entries[idx].identifier) { + ++idx; + idx %= dd->alloc; + } + dd->entries[idx] = e; + } + dd->size = classSize; + assert(d->refCount.load() > 1); + d->refCount.deref(); + d = dd; + } + + uint idx = entry.identifier->hashValue % d->alloc; + while (d->entries[idx].identifier) { + ++idx; + idx %= d->alloc; + } + d->entries[idx] = entry; + ++d->size; +} + +uint PropertyHash::lookup(const Identifier *identifier) const +{ + assert(d->entries); + + uint idx = identifier->hashValue % d->alloc; + while (1) { + if (d->entries[idx].identifier == identifier) + return d->entries[idx].index; + if (!d->entries[idx].identifier) + return UINT_MAX; + ++idx; + idx %= d->alloc; + } +} + + +InternalClass::InternalClass(const QV4::InternalClass &other) + : engine(other.engine) + , propertyTable(other.propertyTable) + , nameMap(other.nameMap) + , propertyData(other.propertyData) + , transitions() + , m_sealed(0) + , m_frozen(0) + , size(other.size) +{ +} + +// ### Should we build this up from the empty class to avoid duplication? +InternalClass *InternalClass::changeMember(String *string, PropertyAttributes data, uint *index) +{ +// qDebug() << "InternalClass::changeMember()" << string->toQString() << hex << (uint)data.m_all; + data.resolve(); + uint idx = find(string); + if (index) + *index = idx; + + assert(idx != UINT_MAX); + + if (data == propertyData[idx]) + return this; + + + Transition t = { string->identifier, (int)data.flags() }; + QHash::const_iterator tit = transitions.constFind(t); + if (tit != transitions.constEnd()) + return tit.value(); + + // create a new class and add it to the tree + InternalClass *newClass = engine->newClass(*this); + newClass->propertyData[idx] = data; + return newClass; + +} + +InternalClass *InternalClass::addMember(String *string, PropertyAttributes data, uint *index) +{ +// qDebug() << "InternalClass::addMember()" << string->toQString() << size << hex << (uint)data.m_all << data.type(); + data.resolve(); + engine->identifierTable->identifier(string); + + if (propertyTable.lookup(string->identifier) < size) + return changeMember(string, data, index); + + Transition t = { string->identifier, (int)data.flags() }; + QHash::const_iterator tit = transitions.constFind(t); + + if (index) + *index = size; + if (tit != transitions.constEnd()) + return tit.value(); + + // create a new class and add it to the tree + InternalClass *newClass = engine->newClass(*this); + PropertyHash::Entry e = { string->identifier, size }; + newClass->propertyTable.addEntry(e, size); + + // The incoming string can come from anywhere, so make sure to + // store a string in the nameMap that's guaranteed to get + // marked properly during GC. + String *name = engine->newIdentifier(string->toQString()); + newClass->nameMap.append(name); + + newClass->propertyData.append(data); + ++newClass->size; + transitions.insert(t, newClass); + return newClass; +} + +void InternalClass::removeMember(Object *object, Identifier *id) +{ + int propIdx = propertyTable.lookup(id); + assert(propIdx < size); + + Transition t = { id, -1 }; + QHash::const_iterator tit = transitions.constFind(t); + + if (tit != transitions.constEnd()) { + object->internalClass = tit.value(); + return; + } + + // create a new class and add it to the tree + object->internalClass = engine->emptyClass; + for (int i = 0; i < nameMap.size(); ++i) { + if (i == propIdx) + continue; + object->internalClass = object->internalClass->addMember(nameMap.at(i), propertyData.at(i)); + } + + transitions.insert(t, object->internalClass); +} + +uint InternalClass::find(String *string) +{ + engine->identifierTable->identifier(string); + const Identifier *id = string->identifier; + + uint index = propertyTable.lookup(id); + if (index < size) + return index; + + return UINT_MAX; +} + +InternalClass *InternalClass::sealed() +{ + if (m_sealed) + return m_sealed; + + m_sealed = engine->emptyClass; + for (int i = 0; i < nameMap.size(); ++i) { + PropertyAttributes attrs = propertyData.at(i); + attrs.setConfigurable(false); + m_sealed = m_sealed->addMember(nameMap.at(i), attrs); + } + + m_sealed->m_sealed = m_sealed; + return m_sealed; +} + +InternalClass *InternalClass::frozen() +{ + if (m_frozen) + return m_frozen; + + m_frozen = engine->emptyClass; + for (int i = 0; i < nameMap.size(); ++i) { + PropertyAttributes attrs = propertyData.at(i); + attrs.setWritable(false); + attrs.setConfigurable(false); + m_frozen = m_frozen->addMember(nameMap.at(i), attrs); + } + + m_frozen->m_frozen = m_frozen; + m_frozen->m_sealed = m_frozen; + return m_frozen; +} + +void InternalClass::destroy() +{ + if (!engine) + return; + engine = 0; + + // Free the memory of the hashes/vectors by calling clear(), which + // re-assigns them to the shared null instance. Therefore Internalclass + // doesn't need a destructor to be called. + propertyTable.~PropertyHash(); + nameMap.clear(); + propertyData.clear(); + + if (m_sealed) + m_sealed->destroy(); + + if (m_frozen) + m_frozen->destroy(); + + for (QHash::ConstIterator it = transitions.begin(), end = transitions.end(); + it != end; ++it) + it.value()->destroy(); + + transitions.clear(); +} + +QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4internalclass_p.h b/src/qml/jsruntime/qv4internalclass_p.h new file mode 100644 index 0000000000..fc6c5352b1 --- /dev/null +++ b/src/qml/jsruntime/qv4internalclass_p.h @@ -0,0 +1,154 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4INTERNALCLASS_H +#define QV4INTERNALCLASS_H + +#include +#include +#include "qv4global_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct String; +struct ExecutionEngine; +struct Object; +struct Identifier; + +struct PropertyHashData; +struct PropertyHash +{ + struct Entry { + const Identifier *identifier; + uint index; + }; + + PropertyHashData *d; + + inline PropertyHash(); + inline PropertyHash(const PropertyHash &other); + inline ~PropertyHash(); + + void addEntry(const Entry &entry, int classSize); + uint lookup(const Identifier *identifier) const; + +private: + PropertyHash &operator=(const PropertyHash &other); +}; + +struct PropertyHashData +{ + PropertyHashData(int numBits); + ~PropertyHashData() { + free(entries); + } + + QBasicAtomicInt refCount; + int alloc; + int size; + int numBits; + PropertyHash::Entry *entries; +}; + +inline PropertyHash::PropertyHash() +{ + d = new PropertyHashData(3); +} + +inline PropertyHash::PropertyHash(const PropertyHash &other) +{ + d = other.d; + d->refCount.ref(); +} + +inline PropertyHash::~PropertyHash() +{ + if (!d->refCount.deref()) + delete d; +} + +struct InternalClassTransition +{ + Identifier *id; + int flags; + + bool operator==(const InternalClassTransition &other) const + { return id == other.id && flags == other.flags; } +}; +uint qHash(const QV4::InternalClassTransition &t, uint = 0); + +struct InternalClass { + ExecutionEngine *engine; + PropertyHash propertyTable; // id to valueIndex + QVector nameMap; + + QVector propertyData; + + typedef InternalClassTransition Transition; + QHash transitions; // id to next class, positive means add, negative delete + + InternalClass *m_sealed; + InternalClass *m_frozen; + + uint size; + + InternalClass *addMember(String *string, PropertyAttributes data, uint *index = 0); + InternalClass *changeMember(String *string, PropertyAttributes data, uint *index = 0); + void removeMember(Object *object, Identifier *id); + uint find(String *s); + + InternalClass *sealed(); + InternalClass *frozen(); + + void destroy(); + +private: + friend struct ExecutionEngine; + InternalClass(ExecutionEngine *engine) : engine(engine), m_sealed(0), m_frozen(0), size(0) {} + InternalClass(const InternalClass &other); +}; + +} + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/jsruntime/qv4jsonobject.cpp b/src/qml/jsruntime/qv4jsonobject.cpp new file mode 100644 index 0000000000..782c388e5a --- /dev/null +++ b/src/qml/jsruntime/qv4jsonobject.cpp @@ -0,0 +1,1040 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace QV4; + +//#define PARSER_DEBUG +#ifdef PARSER_DEBUG +static int indent = 0; +#define BEGIN qDebug() << QByteArray(4*indent++, ' ').constData() +#define END --indent +#define DEBUG qDebug() << QByteArray(4*indent, ' ').constData() +#else +#define BEGIN if (1) ; else qDebug() +#define END do {} while (0) +#define DEBUG if (1) ; else qDebug() +#endif + + +class JsonParser +{ +public: + JsonParser(ExecutionContext *context, const QChar *json, int length); + + Value parse(QJsonParseError *error); + +private: + inline bool eatSpace(); + inline QChar nextToken(); + + Value parseObject(); + Value parseArray(); + bool parseMember(Object *o); + bool parseString(QString *string); + bool parseValue(Value *val); + bool parseNumber(Value *val); + + ExecutionContext *context; + const QChar *head; + const QChar *json; + const QChar *end; + + int nestingLevel; + QJsonParseError::ParseError lastError; +}; + +static const int nestingLimit = 1024; + + +JsonParser::JsonParser(ExecutionContext *context, const QChar *json, int length) + : context(context), head(json), json(json), nestingLevel(0), lastError(QJsonParseError::NoError) +{ + end = json + length; +} + + + +/* + +begin-array = ws %x5B ws ; [ left square bracket + +begin-object = ws %x7B ws ; { left curly bracket + +end-array = ws %x5D ws ; ] right square bracket + +end-object = ws %x7D ws ; } right curly bracket + +name-separator = ws %x3A ws ; : colon + +value-separator = ws %x2C ws ; , comma + +Insignificant whitespace is allowed before or after any of the six +structural characters. + +ws = *( + %x20 / ; Space + %x09 / ; Horizontal tab + %x0A / ; Line feed or New line + %x0D ; Carriage return + ) + +*/ + +enum { + Space = 0x20, + Tab = 0x09, + LineFeed = 0x0a, + Return = 0x0d, + BeginArray = 0x5b, + BeginObject = 0x7b, + EndArray = 0x5d, + EndObject = 0x7d, + NameSeparator = 0x3a, + ValueSeparator = 0x2c, + Quote = 0x22 +}; + +bool JsonParser::eatSpace() +{ + while (json < end) { + if (*json > Space) + break; + if (*json != Space && + *json != Tab && + *json != LineFeed && + *json != Return) + break; + ++json; + } + return (json < end); +} + +QChar JsonParser::nextToken() +{ + if (!eatSpace()) + return 0; + QChar token = *json++; + switch (token.unicode()) { + case BeginArray: + case BeginObject: + case NameSeparator: + case ValueSeparator: + case EndArray: + case EndObject: + eatSpace(); + case Quote: + break; + default: + token = 0; + break; + } + return token; +} + +/* + JSON-text = object / array +*/ +Value JsonParser::parse(QJsonParseError *error) +{ +#ifdef PARSER_DEBUG + indent = 0; + qDebug() << ">>>>> parser begin"; +#endif + + eatSpace(); + + Value v; + if (!parseValue(&v)) { +#ifdef PARSER_DEBUG + qDebug() << ">>>>> parser error"; +#endif + if (lastError == QJsonParseError::NoError) + lastError = QJsonParseError::IllegalValue; + error->offset = json - head; + error->error = lastError; + return Value::undefinedValue(); + } + + // some input left... + if (eatSpace()) { + lastError = QJsonParseError::IllegalValue; + error->offset = json - head; + error->error = lastError; + return Value::undefinedValue(); + } + + END; + error->offset = 0; + error->error = QJsonParseError::NoError; + return v; +} + +/* + object = begin-object [ member *( value-separator member ) ] + end-object +*/ + +Value JsonParser::parseObject() +{ + if (++nestingLevel > nestingLimit) { + lastError = QJsonParseError::DeepNesting; + return Value::undefinedValue(); + } + + BEGIN << "parseObject pos=" << json; + + Object *o = context->engine->newObject(); + Value objectVal = Value::fromObject(o); + + QChar token = nextToken(); + while (token == Quote) { + if (!parseMember(o)) + return Value::undefinedValue(); + token = nextToken(); + if (token != ValueSeparator) + break; + token = nextToken(); + if (token == EndObject) { + lastError = QJsonParseError::MissingObject; + return Value::undefinedValue(); + } + } + + DEBUG << "end token=" << token; + if (token != EndObject) { + lastError = QJsonParseError::UnterminatedObject; + return Value::undefinedValue(); + } + + END; + + --nestingLevel; + return objectVal; +} + +/* + member = string name-separator value +*/ +bool JsonParser::parseMember(Object *o) +{ + BEGIN << "parseMember"; + + QString key; + if (!parseString(&key)) + return false; + QChar token = nextToken(); + if (token != NameSeparator) { + lastError = QJsonParseError::MissingNameSeparator; + return false; + } + Value val; + if (!parseValue(&val)) + return false; + + Property *p = o->insertMember(context->engine->newIdentifier(key), Attr_Data); + p->value = val; + + END; + return true; +} + +/* + array = begin-array [ value *( value-separator value ) ] end-array +*/ +Value JsonParser::parseArray() +{ + BEGIN << "parseArray"; + ArrayObject *array = context->engine->newArrayObject(); + + if (++nestingLevel > nestingLimit) { + lastError = QJsonParseError::DeepNesting; + return Value::undefinedValue(); + } + + if (!eatSpace()) { + lastError = QJsonParseError::UnterminatedArray; + return Value::undefinedValue(); + } + if (*json == EndArray) { + nextToken(); + } else { + uint index = 0; + while (1) { + Value val; + if (!parseValue(&val)) + return Value::undefinedValue(); + array->arraySet(index, val); + QChar token = nextToken(); + if (token == EndArray) + break; + else if (token != ValueSeparator) { + if (!eatSpace()) + lastError = QJsonParseError::UnterminatedArray; + else + lastError = QJsonParseError::MissingValueSeparator; + return Value::undefinedValue(); + } + ++index; + } + } + + DEBUG << "size =" << array->arrayLength(); + END; + + --nestingLevel; + return Value::fromObject(array); +} + +/* +value = false / null / true / object / array / number / string + +*/ + +bool JsonParser::parseValue(Value *val) +{ + BEGIN << "parse Value" << *json; + + switch ((json++)->unicode()) { + case 'n': + if (end - json < 3) { + lastError = QJsonParseError::IllegalValue; + return false; + } + if (*json++ == 'u' && + *json++ == 'l' && + *json++ == 'l') { + *val = Value::nullValue(); + DEBUG << "value: null"; + END; + return true; + } + lastError = QJsonParseError::IllegalValue; + return false; + case 't': + if (end - json < 3) { + lastError = QJsonParseError::IllegalValue; + return false; + } + if (*json++ == 'r' && + *json++ == 'u' && + *json++ == 'e') { + *val = Value::fromBoolean(true); + DEBUG << "value: true"; + END; + return true; + } + lastError = QJsonParseError::IllegalValue; + return false; + case 'f': + if (end - json < 4) { + lastError = QJsonParseError::IllegalValue; + return false; + } + if (*json++ == 'a' && + *json++ == 'l' && + *json++ == 's' && + *json++ == 'e') { + *val = Value::fromBoolean(false); + DEBUG << "value: false"; + END; + return true; + } + lastError = QJsonParseError::IllegalValue; + return false; + case Quote: { + QString value; + if (!parseString(&value)) + return false; + DEBUG << "value: string"; + END; + *val = Value::fromString(context, value); + return true; + } + case BeginArray: { + *val = parseArray(); + if (val->isUndefined()) + return false; + DEBUG << "value: array"; + END; + return true; + } + case BeginObject: { + *val = parseObject(); + if (val->isUndefined()) + return false; + DEBUG << "value: object"; + END; + return true; + } + case EndArray: + lastError = QJsonParseError::MissingObject; + return false; + default: + --json; + if (!parseNumber(val)) + return false; + DEBUG << "value: number"; + END; + } + + return true; +} + + + + + +/* + number = [ minus ] int [ frac ] [ exp ] + decimal-point = %x2E ; . + digit1-9 = %x31-39 ; 1-9 + e = %x65 / %x45 ; e E + exp = e [ minus / plus ] 1*DIGIT + frac = decimal-point 1*DIGIT + int = zero / ( digit1-9 *DIGIT ) + minus = %x2D ; - + plus = %x2B ; + + zero = %x30 ; 0 + +*/ + +bool JsonParser::parseNumber(Value *val) +{ + BEGIN << "parseNumber" << *json; + + const QChar *start = json; + bool isInt = true; + + // minus + if (json < end && *json == '-') + ++json; + + // int = zero / ( digit1-9 *DIGIT ) + if (json < end && *json == '0') { + ++json; + } else { + while (json < end && *json >= '0' && *json <= '9') + ++json; + } + + // frac = decimal-point 1*DIGIT + if (json < end && *json == '.') { + isInt = false; + ++json; + while (json < end && *json >= '0' && *json <= '9') + ++json; + } + + // exp = e [ minus / plus ] 1*DIGIT + if (json < end && (*json == 'e' || *json == 'E')) { + isInt = false; + ++json; + if (json < end && (*json == '-' || *json == '+')) + ++json; + while (json < end && *json >= '0' && *json <= '9') + ++json; + } + + QString number(start, json - start); + DEBUG << "numberstring" << number; + + if (isInt) { + bool ok; + int n = number.toInt(&ok); + if (ok && n < (1<<25) && n > -(1<<25)) { + *val = Value::fromInt32(n); + END; + return true; + } + } + + bool ok; + double d; + d = number.toDouble(&ok); + + if (!ok) { + lastError = QJsonParseError::IllegalNumber; + return false; + } + + * val = Value::fromDouble(d); + + END; + return true; +} + +/* + + string = quotation-mark *char quotation-mark + + char = unescaped / + escape ( + %x22 / ; " quotation mark U+0022 + %x5C / ; \ reverse solidus U+005C + %x2F / ; / solidus U+002F + %x62 / ; b backspace U+0008 + %x66 / ; f form feed U+000C + %x6E / ; n line feed U+000A + %x72 / ; r carriage return U+000D + %x74 / ; t tab U+0009 + %x75 4HEXDIG ) ; uXXXX U+XXXX + + escape = %x5C ; \ + + quotation-mark = %x22 ; " + + unescaped = %x20-21 / %x23-5B / %x5D-10FFFF + */ +static inline bool addHexDigit(QChar digit, uint *result) +{ + ushort d = digit.unicode(); + *result <<= 4; + if (d >= '0' && d <= '9') + *result |= (d - '0'); + else if (d >= 'a' && d <= 'f') + *result |= (d - 'a') + 10; + else if (d >= 'A' && d <= 'F') + *result |= (d - 'A') + 10; + else + return false; + return true; +} + +static inline bool scanEscapeSequence(const QChar *&json, const QChar *end, uint *ch) +{ + ++json; + if (json >= end) + return false; + + DEBUG << "scan escape"; + uint escaped = (json++)->unicode(); + switch (escaped) { + case '"': + *ch = '"'; break; + case '\\': + *ch = '\\'; break; + case '/': + *ch = '/'; break; + case 'b': + *ch = 0x8; break; + case 'f': + *ch = 0xc; break; + case 'n': + *ch = 0xa; break; + case 'r': + *ch = 0xd; break; + case 't': + *ch = 0x9; break; + case 'u': { + *ch = 0; + if (json > end - 4) + return false; + for (int i = 0; i < 4; ++i) { + if (!addHexDigit(*json, ch)) + return false; + ++json; + } + if (*ch <= 0x1f) + return false; + return true; + } + default: + return false; + } + return true; +} + + +bool JsonParser::parseString(QString *string) +{ + BEGIN << "parse string stringPos=" << json; + + while (json < end) { + if (*json == '"') + break; + else if (*json == '\\') { + uint ch = 0; + if (!scanEscapeSequence(json, end, &ch)) { + lastError = QJsonParseError::IllegalEscapeSequence; + return false; + } + qDebug() << "scanEscape" << hex << ch; + if (QChar::requiresSurrogates(ch)) { + *string += QChar::highSurrogate(ch); + *string += QChar::lowSurrogate(ch); + } else { + *string += QChar(ch); + } + } else { + if (json->unicode() <= 0x1f) { + lastError = QJsonParseError::IllegalEscapeSequence; + return false; + } + *string += *json; + ++json; + } + } + ++json; + + if (json > end) { + lastError = QJsonParseError::UnterminatedString; + return false; + } + + END; + return true; +} + + +struct Stringify +{ + ExecutionContext *ctx; + FunctionObject *replacerFunction; + QVector propertyList; + QString gap; + QString indent; + + QStack stack; + + Stringify(ExecutionContext *ctx) : ctx(ctx), replacerFunction(0) {} + + QString Str(const QString &key, Value value); + QString JA(ArrayObject *a); + QString JO(Object *o); + + QString makeMember(const QString &key, Value v); +}; + +static QString quote(const QString &str) +{ + QString product = "\""; + for (int i = 0; i < str.length(); ++i) { + QChar c = str.at(i); + switch (c.unicode()) { + case '"': + product += "\\\""; + break; + case '\\': + product += "\\\\"; + break; + case '\b': + product += "\\b"; + break; + case '\f': + product += "\\f"; + break; + case '\n': + product += "\\n"; + break; + case '\r': + product += "\\r"; + break; + case '\t': + product += "\\t"; + break; + default: + if (c.unicode() <= 0x1f) { + product += "\\u00"; + product += c.unicode() > 0xf ? '1' : '0'; + product += "0123456789abcdef"[c.unicode() & 0xf]; + } else { + product += c; + } + } + } + product += '"'; + return product; +} + +QString Stringify::Str(const QString &key, Value value) +{ + QString result; + + if (Object *o = value.asObject()) { + FunctionObject *toJSON = o->get(ctx->engine->newString(QStringLiteral("toJSON"))).asFunctionObject(); + if (toJSON) { + Value arg = Value::fromString(ctx, key); + value = toJSON->call(value, &arg, 1); + } + } + + if (replacerFunction) { + Object *holder = ctx->engine->newObject(); + Value holderValue = Value::fromObject(holder); + holder->put(ctx, QString(), value); + Value args[2]; + args[0] = Value::fromString(ctx, key); + args[1] = value; + value = replacerFunction->call(holderValue, args, 2); + } + + if (Object *o = value.asObject()) { + if (NumberObject *n = o->asNumberObject()) + value = n->value; + else if (StringObject *so = o->asStringObject()) + value = so->value; + else if (BooleanObject *b =o->asBooleanObject()) + value = b->value; + } + + if (value.isNull()) + return QStringLiteral("null"); + if (value.isBoolean()) + return value.booleanValue() ? QStringLiteral("true") : QStringLiteral("false"); + if (value.isString()) + return quote(value.stringValue()->toQString()); + + if (value.isNumber()) { + double d = value.toNumber(); + return std::isfinite(d) ? value.toString(ctx)->toQString() : QStringLiteral("null"); + } + + if (Object *o = value.asObject()) { + if (!o->asFunctionObject()) { + if (o->asArrayObject()) + return JA(static_cast(o)); + else + return JO(o); + } + } + + return QString(); +} + +QString Stringify::makeMember(const QString &key, Value v) +{ + QString strP = Str(key, v); + if (!strP.isEmpty()) { + QString member = quote(key) + ':'; + if (!gap.isEmpty()) + member += ' '; + member += strP; + return member; + } + return QString(); +} + +QString Stringify::JO(Object *o) +{ + if (stack.contains(o)) + ctx->throwTypeError(); + + QString result; + stack.push(o); + QString stepback = indent; + indent += gap; + + QStringList partial; + if (propertyList.isEmpty()) { + ObjectIterator it(o, ObjectIterator::EnumerableOnly); + + while (1) { + Value v; + Value name = it.nextPropertyNameAsString(&v); + if (name.isNull()) + break; + QString key = name.toQString(); + QString member = makeMember(key, v); + if (!member.isEmpty()) + partial += member; + } + } else { + for (int i = 0; i < propertyList.size(); ++i) { + bool exists; + Value v = o->get(propertyList.at(i), &exists); + if (!exists) + continue; + QString member = makeMember(propertyList.at(i)->toQString(), v); + if (!member.isEmpty()) + partial += member; + } + } + + if (partial.isEmpty()) { + result = QStringLiteral("{}"); + } else if (gap.isEmpty()) { + result = "{" + partial.join(",") + "}"; + } else { + QString separator = ",\n" + indent; + result = "{\n" + indent + partial.join(separator) + "\n" + stepback + "}"; + } + + indent = stepback; + stack.pop(); + return result; +} + +QString Stringify::JA(ArrayObject *a) +{ + if (stack.contains(a)) + ctx->throwTypeError(); + + QString result; + stack.push(a); + QString stepback = indent; + indent += gap; + + QStringList partial; + uint len = a->arrayLength(); + for (uint i = 0; i < len; ++i) { + bool exists; + Value v = a->getIndexed(i, &exists); + if (!exists) { + partial += QStringLiteral("null"); + continue; + } + QString strP = Str(QString::number(i), v); + if (!strP.isEmpty()) + partial += strP; + else + partial += QStringLiteral("null"); + } + + if (partial.isEmpty()) { + result = QStringLiteral("[]"); + } else if (gap.isEmpty()) { + result = "[" + partial.join(",") + "]"; + } else { + QString separator = ",\n" + indent; + result = "[\n" + indent + partial.join(separator) + "\n" + stepback + "]"; + } + + indent = stepback; + stack.pop(); + return result; +} + + +JsonObject::JsonObject(ExecutionContext *context) + : Object(context->engine) +{ + type = Type_JSONObject; + prototype = context->engine->objectPrototype; + + defineDefaultProperty(context, QStringLiteral("parse"), method_parse, 2); + defineDefaultProperty(context, QStringLiteral("stringify"), method_stringify, 3); +} + + +Value JsonObject::method_parse(SimpleCallContext *ctx) +{ + QString jtext = ctx->argument(0).toString(ctx)->toQString(); + + DEBUG << "parsing source = " << jtext; + JsonParser parser(ctx, jtext.constData(), jtext.length()); + QJsonParseError error; + Value result = parser.parse(&error); + if (error.error != QJsonParseError::NoError) { + DEBUG << "parse error" << error.errorString(); + ctx->throwSyntaxError(0); + } + + return result; +} + +Value JsonObject::method_stringify(SimpleCallContext *ctx) +{ + Stringify stringify(ctx); + + Object *o = ctx->argument(1).asObject(); + if (o) { + stringify.replacerFunction = o->asFunctionObject(); + if (o->isArrayObject()) { + uint arrayLen = o->arrayLength(); + for (uint i = 0; i < arrayLen; ++i) { + Value v = o->getIndexed(i); + if (v.asNumberObject() || v.asStringObject() || v.isNumber()) + v = __qmljs_to_string(v, ctx); + if (v.isString()) { + String *s = v.stringValue(); + if (!stringify.propertyList.contains(s)) + stringify.propertyList.append(s); + } + } + } + } + + Value s = ctx->argument(2); + if (NumberObject *n = s.asNumberObject()) + s = n->value; + else if (StringObject *so = s.asStringObject()) + s = so->value; + + if (s.isNumber()) { + stringify.gap = QString(qMin(10, (int)s.toInteger()), ' '); + } else if (s.isString()) { + stringify.gap = s.stringValue()->toQString().left(10); + } + + + QString result = stringify.Str(QString(), ctx->argument(0)); + if (result.isEmpty()) + return Value::undefinedValue(); + return Value::fromString(ctx, result); +} + + + +QV4::Value JsonObject::fromJsonValue(ExecutionEngine *engine, const QJsonValue &value) +{ + if (value.isString()) + return Value::fromString(engine->current, value.toString()); + else if (value.isDouble()) + return Value::fromDouble(value.toDouble()); + else if (value.isBool()) + return Value::fromBoolean(value.toBool()); + else if (value.isArray()) + return fromJsonArray(engine, value.toArray()); + else if (value.isObject()) + return fromJsonObject(engine, value.toObject()); + else if (value.isNull()) + return Value::nullValue(); + else + return Value::undefinedValue(); +} + +QJsonValue JsonObject::toJsonValue(const QV4::Value &value, + V4ObjectSet &visitedObjects) +{ + if (String *s = value.asString()) + return QJsonValue(s->toQString()); + else if (value.isNumber()) + return QJsonValue(value.toNumber()); + else if (value.isBoolean()) + return QJsonValue((bool)value.booleanValue()); + else if (ArrayObject *a = value.asArrayObject()) + return toJsonArray(a, visitedObjects); + else if (Object *o = value.asObject()) + return toJsonObject(o, visitedObjects); + else if (value.isNull()) + return QJsonValue(QJsonValue::Null); + else + return QJsonValue(QJsonValue::Undefined); +} + +QV4::Value JsonObject::fromJsonObject(ExecutionEngine *engine, const QJsonObject &object) +{ + Object *o = engine->newObject(); + for (QJsonObject::const_iterator it = object.begin(); it != object.end(); ++it) + o->put(engine->newString(it.key()), fromJsonValue(engine, it.value())); + return Value::fromObject(o); +} + +QJsonObject JsonObject::toJsonObject(QV4::Object *o, V4ObjectSet &visitedObjects) +{ + QJsonObject result; + if (!o || o->asFunctionObject()) + return result; + + if (visitedObjects.contains(o)) { + // Avoid recursion. + // For compatibility with QVariant{List,Map} conversion, we return an + // empty object (and no error is thrown). + return result; + } + + visitedObjects.insert(o); + + ObjectIterator it(o, ObjectIterator::EnumerableOnly); + while (1) { + Value v; + Value name = it.nextPropertyNameAsString(&v); + if (name.isNull()) + break; + + QString key = name.toQString(); + if (!v.asFunctionObject()) + result.insert(key, toJsonValue(v, visitedObjects)); + } + + visitedObjects.remove(o); + + return result; +} + +QV4::Value JsonObject::fromJsonArray(ExecutionEngine *engine, const QJsonArray &array) +{ + int size = array.size(); + ArrayObject *a = engine->newArrayObject(); + a->arrayReserve(size); + a->arrayDataLen = size; + for (int i = 0; i < size; i++) + a->arrayData[i].value = fromJsonValue(engine, array.at(i)); + a->setArrayLengthUnchecked(size); + return Value::fromObject(a); +} + +QJsonArray JsonObject::toJsonArray(ArrayObject *a, V4ObjectSet &visitedObjects) +{ + QJsonArray result; + if (!a) + return result; + + if (visitedObjects.contains(a)) { + // Avoid recursion. + // For compatibility with QVariant{List,Map} conversion, we return an + // empty array (and no error is thrown). + return result; + } + + visitedObjects.insert(a); + + quint32 length = a->arrayLength(); + for (quint32 i = 0; i < length; ++i) { + Value v = a->getIndexed(i); + result.append(toJsonValue(v.asFunctionObject() ? QV4::Value::nullValue() : v, visitedObjects)); + } + + visitedObjects.remove(a); + + return result; +} diff --git a/src/qml/jsruntime/qv4jsonobject_p.h b/src/qml/jsruntime/qv4jsonobject_p.h new file mode 100644 index 0000000000..ccd99d5488 --- /dev/null +++ b/src/qml/jsruntime/qv4jsonobject_p.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4JSONOBJECTS_H +#define QV4SJONOBJECTS_H + +#include "qv4object_p.h" +#include +#include +#include + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct JsonObject : Object { +private: + typedef QSet V4ObjectSet; +public: + JsonObject(ExecutionContext *context); + + static Value method_parse(SimpleCallContext *ctx); + static Value method_stringify(SimpleCallContext *ctx); + + static QV4::Value fromJsonValue(ExecutionEngine *engine, const QJsonValue &value); + static QV4::Value fromJsonObject(ExecutionEngine *engine, const QJsonObject &object); + static QV4::Value fromJsonArray(ExecutionEngine *engine, const QJsonArray &array); + + static inline QJsonValue toJsonValue(const QV4::Value &value) + { V4ObjectSet visitedObjects; return toJsonValue(value, visitedObjects); } + static inline QJsonObject toJsonObject(QV4::Object *o) + { V4ObjectSet visitedObjects; return toJsonObject(o, visitedObjects); } + static inline QJsonArray toJsonArray(QV4::ArrayObject *a) + { V4ObjectSet visitedObjects; return toJsonArray(a, visitedObjects); } + +private: + static QJsonValue toJsonValue(const QV4::Value &value, V4ObjectSet &visitedObjects); + static QJsonObject toJsonObject(QV4::Object *o, V4ObjectSet &visitedObjects); + static QJsonArray toJsonArray(QV4::ArrayObject *a, V4ObjectSet &visitedObjects); + +}; + +} + +QT_END_NAMESPACE + +#endif + diff --git a/src/qml/jsruntime/qv4lookup.cpp b/src/qml/jsruntime/qv4lookup.cpp new file mode 100644 index 0000000000..b5ea877bd4 --- /dev/null +++ b/src/qml/jsruntime/qv4lookup.cpp @@ -0,0 +1,365 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "qv4lookup_p.h" +#include "qv4functionobject_p.h" + +QT_BEGIN_NAMESPACE + +using namespace QV4; + +Property *Lookup::lookup(Object *obj, PropertyAttributes *attrs) +{ + int i = 0; + while (i < level && obj && obj->internalClass == classList[i]) { + obj = obj->prototype; + ++i; + } + + if (index != UINT_MAX && obj->internalClass == classList[i]) { + *attrs = obj->internalClass->propertyData.at(index); + return obj->memberData + index; + } + + while (i < Size && obj) { + classList[i] = obj->internalClass; + + index = obj->internalClass->find(name); + if (index != UINT_MAX) { + level = i; + *attrs = obj->internalClass->propertyData.at(index); + return obj->memberData + index; + } + + obj = obj->prototype; + ++i; + } + level = i; + + while (obj) { + index = obj->internalClass->find(name); + if (index != UINT_MAX) { + *attrs = obj->internalClass->propertyData.at(index); + return obj->memberData + index; + } + + obj = obj->prototype; + } + return 0; +} + + +void Lookup::getterGeneric(QV4::Lookup *l, QV4::Value *result, const QV4::Value &object) +{ + if (Object *o = object.asObject()) { + o->getLookup(l, result); + return; + } + + Value res; + if (Managed *m = object.asManaged()) { + res = m->get(l->name); + } else { + ExecutionContext *ctx = l->name->engine()->current; + Object *o = __qmljs_convert_to_object(ctx, object); + res = o->get(l->name); + } + if (result) + *result = res; +} + +void Lookup::getter0(Lookup *l, Value *result, const Value &object) +{ + if (Object *o = object.asObject()) { + if (l->classList[0] == o->internalClass) { + if (result) + *result = o->memberData[l->index].value; + return; + } + } + l->getter = getterGeneric; + getterGeneric(l, result, object); +} + +void Lookup::getter1(Lookup *l, Value *result, const Value &object) +{ + if (Object *o = object.asObject()) { + if (l->classList[0] == o->internalClass && + l->classList[1] == o->prototype->internalClass) { + if (result) + *result = o->prototype->memberData[l->index].value; + return; + } + } + l->getter = getterGeneric; + getterGeneric(l, result, object); +} + +void Lookup::getter2(Lookup *l, Value *result, const Value &object) +{ + if (Object *o = object.asObject()) { + if (l->classList[0] == o->internalClass) { + o = o->prototype; + if (l->classList[1] == o->internalClass) { + o = o->prototype; + if (l->classList[2] == o->internalClass) { + if (result) + *result = o->memberData[l->index].value; + return; + } + } + } + } + l->getter = getterGeneric; + getterGeneric(l, result, object); +} + +void Lookup::getterAccessor0(Lookup *l, Value *result, const Value &object) +{ + if (Object *o = object.asObject()) { + if (l->classList[0] == o->internalClass) { + Value res; + FunctionObject *getter = o->memberData[l->index].getter(); + if (!getter) + res = Value::undefinedValue(); + else + res = getter->call(object, 0, 0); + if (result) + *result = res; + return; + } + } + l->getter = getterGeneric; + getterGeneric(l, result, object); +} + +void Lookup::getterAccessor1(Lookup *l, Value *result, const Value &object) +{ + if (Object *o = object.asObject()) { + if (l->classList[0] == o->internalClass && + l->classList[1] == o->prototype->internalClass) { + Value res; + FunctionObject *getter = o->prototype->memberData[l->index].getter(); + if (!getter) + res = Value::undefinedValue(); + else + res = getter->call(object, 0, 0); + if (result) + *result = res; + return; + } + } + l->getter = getterGeneric; + getterGeneric(l, result, object); +} + +void Lookup::getterAccessor2(Lookup *l, Value *result, const Value &object) +{ + if (Object *o = object.asObject()) { + if (l->classList[0] == o->internalClass) { + o = o->prototype; + if (l->classList[1] == o->internalClass) { + o = o->prototype; + if (l->classList[2] == o->internalClass) { + Value res; + FunctionObject *getter = o->memberData[l->index].getter(); + if (!getter) + res = Value::undefinedValue(); + else + res = getter->call(object, 0, 0); + if (result) + *result = res; + return; + } + } + } + } + l->getter = getterGeneric; + getterGeneric(l, result, object); +} + + +void Lookup::globalGetterGeneric(Lookup *l, ExecutionContext *ctx, Value *result) +{ + Object *o = ctx->engine->globalObject; + PropertyAttributes attrs; + Property *p = l->lookup(o, &attrs); + if (p) { + if (attrs.isData()) { + if (l->level == 0) + l->globalGetter = globalGetter0; + else if (l->level == 1) + l->globalGetter = globalGetter1; + else if (l->level == 2) + l->globalGetter = globalGetter2; + *result = p->value; + return; + } else { + if (l->level == 0) + l->globalGetter = globalGetterAccessor0; + else if (l->level == 1) + l->globalGetter = globalGetterAccessor1; + else if (l->level == 2) + l->globalGetter = globalGetterAccessor2; + Value res = o->getValue(p, attrs); + if (result) + *result = res; + return; + } + } + ctx->throwReferenceError(Value::fromString(l->name)); +} + +void Lookup::globalGetter0(Lookup *l, ExecutionContext *ctx, Value *result) +{ + Object *o = ctx->engine->globalObject; + if (l->classList[0] == o->internalClass) { + *result = o->memberData[l->index].value; + return; + } + l->globalGetter = globalGetterGeneric; + globalGetterGeneric(l, ctx, result); +} + +void Lookup::globalGetter1(Lookup *l, ExecutionContext *ctx, Value *result) +{ + Object *o = ctx->engine->globalObject; + if (l->classList[0] == o->internalClass && + l->classList[1] == o->prototype->internalClass) { + *result = o->prototype->memberData[l->index].value; + return; + } + l->globalGetter = globalGetterGeneric; + globalGetterGeneric(l, ctx, result); +} + +void Lookup::globalGetter2(Lookup *l, ExecutionContext *ctx, Value *result) +{ + Object *o = ctx->engine->globalObject; + if (l->classList[0] == o->internalClass) { + o = o->prototype; + if (l->classList[1] == o->internalClass) { + o = o->prototype; + if (l->classList[2] == o->internalClass) { + *result = o->prototype->memberData[l->index].value; + return; + } + } + } + l->globalGetter = globalGetterGeneric; + globalGetterGeneric(l, ctx, result); +} + +void Lookup::globalGetterAccessor0(Lookup *l, ExecutionContext *ctx, Value *result) +{ + Object *o = ctx->engine->globalObject; + if (l->classList[0] == o->internalClass) { + FunctionObject *getter = o->memberData[l->index].getter(); + if (!getter) + *result = Value::undefinedValue(); + else + *result = getter->call(Value::undefinedValue(), 0, 0); + return; + } + l->globalGetter = globalGetterGeneric; + globalGetterGeneric(l, ctx, result); +} + +void Lookup::globalGetterAccessor1(Lookup *l, ExecutionContext *ctx, Value *result) +{ + Object *o = ctx->engine->globalObject; + if (l->classList[0] == o->internalClass && + l->classList[1] == o->prototype->internalClass) { + FunctionObject *getter = o->prototype->memberData[l->index].getter(); + if (!getter) + *result = Value::undefinedValue(); + else + *result = getter->call(Value::undefinedValue(), 0, 0); + return; + } + l->globalGetter = globalGetterGeneric; + globalGetterGeneric(l, ctx, result); +} + +void Lookup::globalGetterAccessor2(Lookup *l, ExecutionContext *ctx, Value *result) +{ + Object *o = ctx->engine->globalObject; + if (l->classList[0] == o->internalClass) { + o = o->prototype; + if (l->classList[1] == o->internalClass) { + o = o->prototype; + if (l->classList[2] == o->internalClass) { + FunctionObject *getter = o->memberData[l->index].getter(); + if (!getter) + *result = Value::undefinedValue(); + else + *result = getter->call(Value::undefinedValue(), 0, 0); + return; + } + } + } + l->globalGetter = globalGetterGeneric; + globalGetterGeneric(l, ctx, result); +} + +void Lookup::setterGeneric(Lookup *l, const Value &object, const Value &value) +{ + Object *o = object.asObject(); + if (!o) { + o = __qmljs_convert_to_object(l->name->engine()->current, object); + o->put(l->name, value); + return; + } + o->setLookup(l, value); +} + +void Lookup::setter0(Lookup *l, const Value &object, const Value &value) +{ + Object *o = object.asObject(); + if (o && o->internalClass == l->classList[0]) { + o->memberData[l->index].value = value; + return; + } + + l->setter = setterGeneric; + setterGeneric(l, object, value); +} + +QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4lookup_p.h b/src/qml/jsruntime/qv4lookup_p.h new file mode 100644 index 0000000000..e77552826a --- /dev/null +++ b/src/qml/jsruntime/qv4lookup_p.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4LOOKUP_H +#define QV4LOOKUP_H + +#include "qv4global_p.h" +#include "qv4runtime_p.h" +#include "qv4engine_p.h" +#include "qv4context_p.h" +#include "qv4object_p.h" +#include "qv4internalclass_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct Lookup { + enum { Size = 3 }; + union { + void (*getter)(Lookup *l, Value *result, const Value &object); + void (*globalGetter)(Lookup *l, ExecutionContext *ctx, Value *result); + void (*setter)(Lookup *l, const Value &object, const Value &v); + }; + InternalClass *classList[Size]; + int level; + uint index; + String *name; + + static void getterGeneric(Lookup *l, Value *result, const Value &object); + static void getter0(Lookup *l, Value *result, const Value &object); + static void getter1(Lookup *l, Value *result, const Value &object); + static void getter2(Lookup *l, Value *result, const Value &object); + static void getterAccessor0(Lookup *l, Value *result, const Value &object); + static void getterAccessor1(Lookup *l, Value *result, const Value &object); + static void getterAccessor2(Lookup *l, Value *result, const Value &object); + + static void globalGetterGeneric(Lookup *l, ExecutionContext *ctx, Value *result); + static void globalGetter0(Lookup *l, ExecutionContext *ctx, Value *result); + static void globalGetter1(Lookup *l, ExecutionContext *ctx, Value *result); + static void globalGetter2(Lookup *l, ExecutionContext *ctx, Value *result); + static void globalGetterAccessor0(Lookup *l, ExecutionContext *ctx, Value *result); + static void globalGetterAccessor1(Lookup *l, ExecutionContext *ctx, Value *result); + static void globalGetterAccessor2(Lookup *l, ExecutionContext *ctx, Value *result); + + static void setterGeneric(Lookup *l, const Value &object, const Value &value); + static void setter0(Lookup *l, const Value &object, const Value &value); + + Property *lookup(Object *obj, PropertyAttributes *attrs); + +}; + +} + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/jsruntime/qv4managed.cpp b/src/qml/jsruntime/qv4managed.cpp new file mode 100644 index 0000000000..19adb354e3 --- /dev/null +++ b/src/qml/jsruntime/qv4managed.cpp @@ -0,0 +1,212 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4managed_p.h" +#include "qv4mm_p.h" +#include "qv4errorobject_p.h" + +using namespace QV4; + +const ManagedVTable Managed::static_vtbl = +{ + call, + construct, + 0 /*markObjects*/, + destroy, + 0 /*collectDeletables*/, + hasInstance, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + isEqualTo, + 0, + "Managed", +}; + + +void *Managed::operator new(size_t size, MemoryManager *mm) +{ + assert(mm); + + return mm->allocManaged(size); +} + +void Managed::operator delete(void *ptr) +{ + if (!ptr) + return; + + Managed *m = static_cast(ptr); + m->vtbl = 0; + m->_data = 0; + m->~Managed(); +} + +void Managed::operator delete(void *ptr, MemoryManager *mm) +{ + Q_UNUSED(mm); + + operator delete(ptr); +} + +ExecutionEngine *Managed::engine() const +{ + return internalClass ? internalClass->engine : 0; +} + +QString Managed::className() const +{ + const char *s = 0; + switch (Type(type)) { + case Type_Invalid: + case Type_String: + return QString(); + case Type_Object: + s = "Object"; + break; + case Type_ArrayObject: + s = "Array"; + break; + case Type_FunctionObject: + s = "Function"; + break; + case Type_BooleanObject: + s = "Boolean"; + break; + case Type_NumberObject: + s = "Number"; + break; + case Type_StringObject: + s = "String"; + break; + case Type_DateObject: + s = "Date"; + break; + case Type_RegExpObject: + s = "RegExp"; + break; + case Type_ErrorObject: + switch (ErrorObject::ErrorType(subtype)) { + case ErrorObject::Error: + s = "Error"; + break; + case ErrorObject::EvalError: + s = "EvalError"; + break; + case ErrorObject::RangeError: + s = "RangeError"; + break; + case ErrorObject::ReferenceError: + s = "ReferenceError"; + break; + case ErrorObject::SyntaxError: + s = "SyntaxError"; + break; + case ErrorObject::TypeError: + s = "TypeError"; + break; + case ErrorObject::URIError: + s = "URIError"; + break; + } + break; + case Type_ArgumentsObject: + s = "Arguments"; + break; + case Type_JSONObject: + s = "JSON"; + break; + case Type_MathObject: + s = "Math"; + break; + case Type_ForeachIteratorObject: + s = "__ForeachIterator"; + break; + } + return QString::fromLatin1(s); +} + +bool Managed::hasInstance(Managed *m, const Value &) +{ + m->engine()->current->throwTypeError(); +} + +Value Managed::construct(Managed *m, Value *, int) +{ + m->engine()->current->throwTypeError(); +} + +Value Managed::call(Managed *m, const Value &, Value *, int) +{ + m->engine()->current->throwTypeError(); +} + +void Managed::getLookup(Managed *m, Lookup *, Value *) +{ + m->engine()->current->throwTypeError(); +} + +void Managed::setLookup(Managed *m, Lookup *, const Value &) +{ + m->engine()->current->throwTypeError(); +} + +bool Managed::isEqualTo(Managed *, Managed *) +{ + return false; +} + +Value Managed::get(String *name, bool *hasProperty) +{ + return vtbl->get(this, name, hasProperty); +} + +Value Managed::getIndexed(uint index, bool *hasProperty) +{ + return vtbl->getIndexed(this, index, hasProperty); +} diff --git a/src/qml/jsruntime/qv4managed_p.h b/src/qml/jsruntime/qv4managed_p.h new file mode 100644 index 0000000000..5e3c142bb8 --- /dev/null +++ b/src/qml/jsruntime/qv4managed_p.h @@ -0,0 +1,321 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QMLJS_MANAGED_H +#define QMLJS_MANAGED_H + +#include +#include +#include +#include "qv4global_p.h" +#include "qv4value_def_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +#define Q_MANAGED_CHECK \ + template inline void qt_check_for_QMANAGED_macro(const T &_q_argument) const \ + { int i = qYouForgotTheQ_MANAGED_Macro(this, &_q_argument); i = i + 1; } + +template +inline int qYouForgotTheQ_MANAGED_Macro(T, T) { return 0; } + +template +inline void qYouForgotTheQ_MANAGED_Macro(T1, T2) {} + +#define Q_MANAGED \ + public: \ + Q_MANAGED_CHECK \ + static const QV4::ManagedVTable static_vtbl; + +struct GCDeletable +{ + GCDeletable() : next(0), lastCall(false) {} + virtual ~GCDeletable() {} + GCDeletable *next; + bool lastCall; +}; + +struct ManagedVTable +{ + Value (*call)(Managed *, const Value &thisObject, Value *args, int argc); + Value (*construct)(Managed *, Value *args, int argc); + void (*markObjects)(Managed *); + void (*destroy)(Managed *); + void (*collectDeletables)(Managed *, GCDeletable **deletable); + bool (*hasInstance)(Managed *, const Value &value); + Value (*get)(Managed *, String *name, bool *hasProperty); + Value (*getIndexed)(Managed *, uint index, bool *hasProperty); + void (*put)(Managed *, String *name, const Value &value); + void (*putIndexed)(Managed *, uint index, const Value &value); + PropertyAttributes (*query)(const Managed *, String *name); + PropertyAttributes (*queryIndexed)(const Managed *, uint index); + bool (*deleteProperty)(Managed *m, String *name); + bool (*deleteIndexedProperty)(Managed *m, uint index); + void (*getLookup)(Managed *m, Lookup *l, Value *result); + void (*setLookup)(Managed *m, Lookup *l, const Value &v); + bool (*isEqualTo)(Managed *m, Managed *other); + Property *(*advanceIterator)(Managed *m, ObjectIterator *it, String **name, uint *index, PropertyAttributes *attributes); + const char *className; +}; + +#define DEFINE_MANAGED_VTABLE(classname) \ +const QV4::ManagedVTable classname::static_vtbl = \ +{ \ + call, \ + construct, \ + markObjects, \ + destroy, \ + 0, \ + hasInstance, \ + get, \ + getIndexed, \ + put, \ + putIndexed, \ + query, \ + queryIndexed, \ + deleteProperty, \ + deleteIndexedProperty, \ + getLookup, \ + setLookup, \ + isEqualTo, \ + advanceIterator, \ + #classname \ +} + +#define DEFINE_MANAGED_VTABLE_WITH_DELETABLES(classname) \ +const QV4::ManagedVTable classname::static_vtbl = \ +{ \ + call, \ + construct, \ + markObjects, \ + destroy, \ + collectDeletables, \ + hasInstance, \ + get, \ + getIndexed, \ + put, \ + putIndexed, \ + query, \ + queryIndexed, \ + deleteProperty, \ + deleteIndexedProperty, \ + getLookup, \ + setLookup, \ + isEqualTo, \ + advanceIterator, \ + #classname \ +} + +struct Q_QML_EXPORT Managed +{ +private: + void *operator new(size_t); + Managed(const Managed &other); + void operator = (const Managed &other); + +protected: + Managed(InternalClass *internal) + : _data(0), vtbl(&static_vtbl), internalClass(internal) + { inUse = 1; extensible = 1; } + +public: + void *operator new(size_t size, MemoryManager *mm); + void operator delete(void *ptr); + void operator delete(void *ptr, MemoryManager *mm); + + inline void mark() { + if (markBit) + return; + markBit = 1; + if (vtbl->markObjects) + vtbl->markObjects(this); + } + + enum Type { + Type_Invalid, + Type_String, + Type_Object, + Type_ArrayObject, + Type_FunctionObject, + Type_BooleanObject, + Type_NumberObject, + Type_StringObject, + Type_DateObject, + Type_RegExpObject, + Type_ErrorObject, + Type_ArgumentsObject, + Type_JSONObject, + Type_MathObject, + Type_ForeachIteratorObject, + Type_RegExp, + + Type_QmlSequence + }; + + ExecutionEngine *engine() const; + + template + T *as() { +#if !defined(QT_NO_QOBJECT_CHECK) + reinterpret_cast(this)->qt_check_for_QMANAGED_macro(*reinterpret_cast(this)); +#endif + return vtbl == &T::static_vtbl ? static_cast(this) : 0; + } + template + const T *as() const { +#if !defined(QT_NO_QOBJECT_CHECK) + reinterpret_cast(this)->qt_check_for_QMANAGED_macro(*reinterpret_cast(const_cast(this))); +#endif + return vtbl == &T::static_vtbl ? static_cast(this) : 0; + } + + ArrayObject *asArrayObject() { return type == Type_ArrayObject ? reinterpret_cast(this) : 0; } + FunctionObject *asFunctionObject() { return type == Type_FunctionObject ? reinterpret_cast(this) : 0; } + BooleanObject *asBooleanObject() { return type == Type_BooleanObject ? reinterpret_cast(this) : 0; } + NumberObject *asNumberObject() { return type == Type_NumberObject ? reinterpret_cast(this) : 0; } + StringObject *asStringObject() { return type == Type_StringObject ? reinterpret_cast(this) : 0; } + DateObject *asDateObject() { return type == Type_DateObject ? reinterpret_cast(this) : 0; } + ErrorObject *asErrorObject() { return type == Type_ErrorObject ? reinterpret_cast(this) : 0; } + ArgumentsObject *asArgumentsObject() { return type == Type_ArgumentsObject ? reinterpret_cast(this) : 0; } + + bool isListType() const { return type == Type_QmlSequence; } + + bool isArrayObject() const { return type == Type_ArrayObject; } + bool isStringObject() const { return type == Type_StringObject; } + + QString className() const; + + Managed **nextFreeRef() { + return reinterpret_cast(this); + } + Managed *nextFree() { + return *reinterpret_cast(this); + } + void setNextFree(Managed *m) { + *reinterpret_cast(this) = m; + } + + inline bool hasInstance(const Value &v) { + return vtbl->hasInstance(this, v); + } + Value construct(Value *args, int argc); + Value call(const Value &thisObject, Value *args, int argc); + Value get(String *name, bool *hasProperty = 0); + Value getIndexed(uint index, bool *hasProperty = 0); + void put(String *name, const Value &value) + { vtbl->put(this, name, value); } + void putIndexed(uint index, const Value &value) + { vtbl->putIndexed(this, index, value); } + PropertyAttributes query(String *name) const + { return vtbl->query(this, name); } + PropertyAttributes queryIndexed(uint index) const + { return vtbl->queryIndexed(this, index); } + + bool deleteProperty(String *name) + { return vtbl->deleteProperty(this, name); } + bool deleteIndexedProperty(uint index) + { return vtbl->deleteIndexedProperty(this, index); } + void getLookup(Lookup *l, Value *result) + { vtbl->getLookup(this, l, result); } + void setLookup(Lookup *l, const Value &v) + { vtbl->setLookup(this, l, v); } + + bool isEqualTo(Managed *other) + { return vtbl->isEqualTo(this, other); } + Property *advanceIterator(ObjectIterator *it, String **name, uint *index, PropertyAttributes *attributes) + { return vtbl->advanceIterator(this, it, name, index, attributes); } + + static void destroy(Managed *that) { that->_data = 0; } + static bool hasInstance(Managed *that, const Value &value); + static Value construct(Managed *m, Value *, int); + static Value call(Managed *m, const Value &, Value *, int); + static void getLookup(Managed *m, Lookup *, Value *); + static void setLookup(Managed *m, Lookup *l, const Value &v); + static bool isEqualTo(Managed *m, Managed *other); + + uint internalType() const { + return type; + } + + union { + uint _data; + struct { + uint markBit : 1; + uint inUse : 1; + uint extensible : 1; // used by Object + uint isNonStrictArgumentsObject : 1; + uint isBuiltinFunction : 1; // used by FunctionObject + uint needsActivation : 1; // used by FunctionObject + uint usesArgumentsObject : 1; // used by FunctionObject + uint strictMode : 1; // used by FunctionObject + uint type : 8; + mutable uint subtype : 3; + uint bindingKeyFlag : 1; + uint unused : 12; + }; + }; + +protected: + + static const ManagedVTable static_vtbl; + + const ManagedVTable *vtbl; +public: + InternalClass *internalClass; + +private: + friend class MemoryManager; + friend struct Identifiers; + friend struct ObjectIterator; +}; + +// ### Not a good placement +template +inline T *Value::as() const { Managed *m = isObject() ? managed() : 0; return m ? m->as() : 0; } + + +} + + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/jsruntime/qv4math_p.h b/src/qml/jsruntime/qv4math_p.h new file mode 100644 index 0000000000..a3a3715545 --- /dev/null +++ b/src/qml/jsruntime/qv4math_p.h @@ -0,0 +1,147 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QMLJS_MATH_H +#define QMLJS_MATH_H + +#include + +#ifndef QMLJS_LLVM_RUNTIME +# include +#endif // QMLJS_LLVM_RUNTIME +#include + +#if defined(Q_CC_GNU) +#define QMLJS_READONLY __attribute((const)) +#else +#define QMLJS_READONLY +#endif + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +#if !defined(QMLJS_LLVM_RUNTIME) && defined(Q_CC_GNU) && defined(Q_PROCESSOR_X86) + +static inline QMLJS_READONLY Value add_int32(int a, int b) +{ + quint8 overflow = 0; + int aa = a; + + asm ("addl %2, %1\n" + "seto %0" + : "=q" (overflow), "=r" (aa) + : "r" (b), "1" (aa) + : "cc" + ); + if (!overflow) + return Value::fromInt32(aa); + return Value::fromDouble((double)a + (double)b); +} + +static inline QMLJS_READONLY Value sub_int32(int a, int b) +{ + quint8 overflow = 0; + int aa = a; + + asm ("subl %2, %1\n" + "seto %0" + : "=q" (overflow), "=r" (aa) + : "r" (b), "1" (aa) + : "cc" + ); + if (!overflow) + return Value::fromInt32(aa); + return Value::fromDouble((double)a - (double)b); +} + +static inline QMLJS_READONLY Value mul_int32(int a, int b) +{ + quint8 overflow = 0; + int aa = a; + + asm ("imul %2, %1\n" + "setc %0" + : "=q" (overflow), "=r" (aa) + : "r" (b), "1" (aa) + : "cc" + ); + if (!overflow) + return Value::fromInt32(aa); + return Value::fromDouble((double)a * (double)b); +} + +#else + +static inline QMLJS_READONLY Value add_int32(int a, int b) +{ + qint64 result = a + b; + if (result > INT_MAX || result < INT_MIN) + return Value::fromDouble(result); + return Value::fromInt32(static_cast(result)); +} + +static inline QMLJS_READONLY Value sub_int32(int a, int b) +{ + qint64 result = a - b; + if (result > INT_MAX || result < INT_MIN) + return Value::fromDouble(result); + return Value::fromInt32(static_cast(result)); +} + +static inline QMLJS_READONLY Value mul_int32(int a, int b) +{ + qint64 result = a * b; + if (result > INT_MAX || result < INT_MIN) + return Value::fromDouble(result); + return Value::fromInt32(static_cast(result)); +} + +#endif // defined(QMLJS_INLINE_MATH) + +} + +QT_END_NAMESPACE + +#ifdef QMLJS_READONLY +#undef QMLJS_READONLY +#endif + +#endif // QMLJS_MATH_H diff --git a/src/qml/jsruntime/qv4mathobject.cpp b/src/qml/jsruntime/qv4mathobject.cpp new file mode 100644 index 0000000000..7aa56f51bd --- /dev/null +++ b/src/qml/jsruntime/qv4mathobject.cpp @@ -0,0 +1,311 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4mathobject_p.h" +#include "qv4objectproto_p.h" + +#include +#include +#include + +using namespace QV4; + +static const double qt_PI = 2.0 * ::asin(1.0); + +MathObject::MathObject(ExecutionContext *ctx) + : Object(ctx->engine) +{ + type = Type_MathObject; + prototype = ctx->engine->objectPrototype; + + defineReadonlyProperty(ctx->engine, QStringLiteral("E"), Value::fromDouble(::exp(1.0))); + defineReadonlyProperty(ctx->engine, QStringLiteral("LN2"), Value::fromDouble(::log(2.0))); + defineReadonlyProperty(ctx->engine, QStringLiteral("LN10"), Value::fromDouble(::log(10.0))); + defineReadonlyProperty(ctx->engine, QStringLiteral("LOG2E"), Value::fromDouble(1.0/::log(2.0))); + defineReadonlyProperty(ctx->engine, QStringLiteral("LOG10E"), Value::fromDouble(1.0/::log(10.0))); + defineReadonlyProperty(ctx->engine, QStringLiteral("PI"), Value::fromDouble(qt_PI)); + defineReadonlyProperty(ctx->engine, QStringLiteral("SQRT1_2"), Value::fromDouble(::sqrt(0.5))); + defineReadonlyProperty(ctx->engine, QStringLiteral("SQRT2"), Value::fromDouble(::sqrt(2.0))); + + defineDefaultProperty(ctx, QStringLiteral("abs"), method_abs, 1); + defineDefaultProperty(ctx, QStringLiteral("acos"), method_acos, 1); + defineDefaultProperty(ctx, QStringLiteral("asin"), method_asin, 0); + defineDefaultProperty(ctx, QStringLiteral("atan"), method_atan, 1); + defineDefaultProperty(ctx, QStringLiteral("atan2"), method_atan2, 2); + defineDefaultProperty(ctx, QStringLiteral("ceil"), method_ceil, 1); + defineDefaultProperty(ctx, QStringLiteral("cos"), method_cos, 1); + defineDefaultProperty(ctx, QStringLiteral("exp"), method_exp, 1); + defineDefaultProperty(ctx, QStringLiteral("floor"), method_floor, 1); + defineDefaultProperty(ctx, QStringLiteral("log"), method_log, 1); + defineDefaultProperty(ctx, QStringLiteral("max"), method_max, 2); + defineDefaultProperty(ctx, QStringLiteral("min"), method_min, 2); + defineDefaultProperty(ctx, QStringLiteral("pow"), method_pow, 2); + defineDefaultProperty(ctx, QStringLiteral("random"), method_random, 0); + defineDefaultProperty(ctx, QStringLiteral("round"), method_round, 1); + defineDefaultProperty(ctx, QStringLiteral("sin"), method_sin, 1); + defineDefaultProperty(ctx, QStringLiteral("sqrt"), method_sqrt, 1); + defineDefaultProperty(ctx, QStringLiteral("tan"), method_tan, 1); +} + +/* copies the sign from y to x and returns the result */ +static double copySign(double x, double y) +{ + uchar *xch = (uchar *)&x; + uchar *ych = (uchar *)&y; + if (QSysInfo::ByteOrder == QSysInfo::BigEndian) + xch[0] = (xch[0] & 0x7f) | (ych[0] & 0x80); + else + xch[7] = (xch[7] & 0x7f) | (ych[7] & 0x80); + return x; +} + +Value MathObject::method_abs(SimpleCallContext *context) +{ + if (!context->argumentCount) + return Value::fromDouble(qSNaN()); + + if (context->arguments[0].isInteger()) { + int i = context->arguments[0].integerValue(); + return Value::fromInt32(i < 0 ? - i : i); + } + + double v = context->arguments[0].toNumber(); + if (v == 0) // 0 | -0 + return Value::fromDouble(0); + + return Value::fromDouble(v < 0 ? -v : v); +} + +Value MathObject::method_acos(SimpleCallContext *context) +{ + double v = context->argumentCount ? context->arguments[0].toNumber() : 2; + if (v > 1) + return Value::fromDouble(qSNaN()); + + return Value::fromDouble(::acos(v)); +} + +Value MathObject::method_asin(SimpleCallContext *context) +{ + double v = context->argumentCount ? context->arguments[0].toNumber() : 2; + if (v > 1) + return Value::fromDouble(qSNaN()); + else + return Value::fromDouble(::asin(v)); +} + +Value MathObject::method_atan(SimpleCallContext *context) +{ + double v = context->argumentCount ? context->arguments[0].toNumber() : qSNaN(); + if (v == 0.0) + return Value::fromDouble(v); + else + return Value::fromDouble(::atan(v)); +} + +Value MathObject::method_atan2(SimpleCallContext *context) +{ + double v1 = context->argumentCount ? context->arguments[0].toNumber() : qSNaN(); + double v2 = context->argumentCount > 1 ? context->arguments[1].toNumber() : qSNaN(); + + if ((v1 < 0) && qIsFinite(v1) && qIsInf(v2) && (copySign(1.0, v2) == 1.0)) + return Value::fromDouble(copySign(0, -1.0)); + + if ((v1 == 0.0) && (v2 == 0.0)) { + if ((copySign(1.0, v1) == 1.0) && (copySign(1.0, v2) == -1.0)) { + return Value::fromDouble(qt_PI); + } else if ((copySign(1.0, v1) == -1.0) && (copySign(1.0, v2) == -1.0)) { + return Value::fromDouble(-qt_PI); + } + } + return Value::fromDouble(::atan2(v1, v2)); +} + +Value MathObject::method_ceil(SimpleCallContext *context) +{ + double v = context->argumentCount ? context->arguments[0].toNumber() : qSNaN(); + if (v < 0.0 && v > -1.0) + return Value::fromDouble(copySign(0, -1.0)); + else + return Value::fromDouble(::ceil(v)); +} + +Value MathObject::method_cos(SimpleCallContext *context) +{ + double v = context->argumentCount ? context->arguments[0].toNumber() : qSNaN(); + return Value::fromDouble(::cos(v)); +} + +Value MathObject::method_exp(SimpleCallContext *context) +{ + double v = context->argumentCount ? context->arguments[0].toNumber() : qSNaN(); + if (qIsInf(v)) { + if (copySign(1.0, v) == -1.0) + return Value::fromDouble(0); + else + return Value::fromDouble(qInf()); + } else { + return Value::fromDouble(::exp(v)); + } +} + +Value MathObject::method_floor(SimpleCallContext *context) +{ + double v = context->argumentCount ? context->arguments[0].toNumber() : qSNaN(); + return Value::fromDouble(::floor(v)); +} + +Value MathObject::method_log(SimpleCallContext *context) +{ + double v = context->argumentCount ? context->arguments[0].toNumber() : qSNaN(); + if (v < 0) + return Value::fromDouble(qSNaN()); + else + return Value::fromDouble(::log(v)); +} + +Value MathObject::method_max(SimpleCallContext *context) +{ + double mx = -qInf(); + for (unsigned i = 0; i < context->argumentCount; ++i) { + double x = context->arguments[i].toNumber(); + if (x > mx || std::isnan(x)) + mx = x; + } + return Value::fromDouble(mx); +} + +Value MathObject::method_min(SimpleCallContext *context) +{ + double mx = qInf(); + for (unsigned i = 0; i < context->argumentCount; ++i) { + double x = context->arguments[i].toNumber(); + if ((x == 0 && mx == x && copySign(1.0, x) == -1.0) + || (x < mx) || std::isnan(x)) { + mx = x; + } + } + return Value::fromDouble(mx); +} + +Value MathObject::method_pow(SimpleCallContext *context) +{ + double x = context->argumentCount > 0 ? context->arguments[0].toNumber() : qSNaN(); + double y = context->argumentCount > 1 ? context->arguments[1].toNumber() : qSNaN(); + + if (std::isnan(y)) + return Value::fromDouble(qSNaN()); + + if (y == 0) { + return Value::fromDouble(1); + } else if (((x == 1) || (x == -1)) && std::isinf(y)) { + return Value::fromDouble(qSNaN()); + } else if (((x == 0) && copySign(1.0, x) == 1.0) && (y < 0)) { + return Value::fromDouble(qInf()); + } else if ((x == 0) && copySign(1.0, x) == -1.0) { + if (y < 0) { + if (::fmod(-y, 2.0) == 1.0) + return Value::fromDouble(-qInf()); + else + return Value::fromDouble(qInf()); + } else if (y > 0) { + if (::fmod(y, 2.0) == 1.0) + return Value::fromDouble(copySign(0, -1.0)); + else + return Value::fromDouble(0); + } + } + +#ifdef Q_OS_AIX + else if (qIsInf(x) && copySign(1.0, x) == -1.0) { + if (y > 0) { + if (::fmod(y, 2.0) == 1.0) + return Value::fromDouble(-qInf()); + else + return Value::fromDouble(qInf()); + } else if (y < 0) { + if (::fmod(-y, 2.0) == 1.0) + return Value::fromDouble(copySign(0, -1.0)); + else + return Value::fromDouble(0); + } + } +#endif + else { + return Value::fromDouble(::pow(x, y)); + } + // ### + return Value::fromDouble(qSNaN()); +} + +Value MathObject::method_random(SimpleCallContext *) +{ + return Value::fromDouble(qrand() / (double) RAND_MAX); +} + +Value MathObject::method_round(SimpleCallContext *context) +{ + double v = context->argumentCount ? context->arguments[0].toNumber() : qSNaN(); + v = copySign(::floor(v + 0.5), v); + return Value::fromDouble(v); +} + +Value MathObject::method_sin(SimpleCallContext *context) +{ + double v = context->argumentCount ? context->arguments[0].toNumber() : qSNaN(); + return Value::fromDouble(::sin(v)); +} + +Value MathObject::method_sqrt(SimpleCallContext *context) +{ + double v = context->argumentCount ? context->arguments[0].toNumber() : qSNaN(); + return Value::fromDouble(::sqrt(v)); +} + +Value MathObject::method_tan(SimpleCallContext *context) +{ + double v = context->argumentCount ? context->arguments[0].toNumber() : qSNaN(); + if (v == 0.0) + return Value::fromDouble(v); + else + return Value::fromDouble(::tan(v)); +} + diff --git a/src/qml/jsruntime/qv4mathobject_p.h b/src/qml/jsruntime/qv4mathobject_p.h new file mode 100644 index 0000000000..03c36bcc68 --- /dev/null +++ b/src/qml/jsruntime/qv4mathobject_p.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4MATHOBJECT_H +#define QV$MATHOBJECT_H + +#include "qv4object_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct MathObject: Object +{ + MathObject(ExecutionContext *ctx); + + static Value method_abs(SimpleCallContext *context); + static Value method_acos(SimpleCallContext *context); + static Value method_asin(SimpleCallContext *context); + static Value method_atan(SimpleCallContext *context); + static Value method_atan2(SimpleCallContext *context); + static Value method_ceil(SimpleCallContext *context); + static Value method_cos(SimpleCallContext *context); + static Value method_exp(SimpleCallContext *context); + static Value method_floor(SimpleCallContext *context); + static Value method_log(SimpleCallContext *context); + static Value method_max(SimpleCallContext *context); + static Value method_min(SimpleCallContext *context); + static Value method_pow(SimpleCallContext *context); + static Value method_random(SimpleCallContext *context); + static Value method_round(SimpleCallContext *context); + static Value method_sin(SimpleCallContext *context); + static Value method_sqrt(SimpleCallContext *context); + static Value method_tan(SimpleCallContext *context); +}; + +} + +QT_END_NAMESPACE + +#endif // QMLJS_OBJECTS_H diff --git a/src/qml/jsruntime/qv4mm.cpp b/src/qml/jsruntime/qv4mm.cpp new file mode 100644 index 0000000000..0e53a2088f --- /dev/null +++ b/src/qml/jsruntime/qv4mm.cpp @@ -0,0 +1,605 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4engine_p.h" +#include "qv4object_p.h" +#include "qv4objectproto_p.h" +#include "qv4mm_p.h" +#include "qv4qobjectwrapper_p.h" +#include +#include "PageAllocation.h" +#include "StdLibExtras.h" + +#include +#include +#include +#include + +#include +#include +#include "qv4alloca_p.h" + +#ifdef V4_USE_VALGRIND +#include +#include +#endif + +#if OS(QNX) +#include // __tls() +#endif + +QT_BEGIN_NAMESPACE + +using namespace QV4; +using namespace WTF; + +static const std::size_t CHUNK_SIZE = 1024*32; + +struct MemoryManager::Data +{ + bool enableGC; + bool gcBlocked; + bool scribble; + bool aggressiveGC; + ExecutionEngine *engine; + quintptr *stackTop; + + enum { MaxItemSize = 512 }; + Managed *smallItems[MaxItemSize/16]; + uint nChunks[MaxItemSize/16]; + uint availableItems[MaxItemSize/16]; + uint allocCount[MaxItemSize/16]; + struct Chunk { + PageAllocation memory; + int chunkSize; + }; + + QVector heapChunks; + QHash protectedObject; + + // statistics: +#ifdef DETAILED_MM_STATS + QVector allocSizeCounters; +#endif // DETAILED_MM_STATS + + Data(bool enableGC) + : enableGC(enableGC) + , gcBlocked(false) + , engine(0) + , stackTop(0) + { + memset(smallItems, 0, sizeof(smallItems)); + memset(nChunks, 0, sizeof(nChunks)); + memset(availableItems, 0, sizeof(availableItems)); + memset(allocCount, 0, sizeof(allocCount)); + scribble = !qgetenv("MM_SCRIBBLE").isEmpty(); + aggressiveGC = !qgetenv("MM_AGGRESSIVE_GC").isEmpty(); + } + + ~Data() + { + for (QVector::iterator i = heapChunks.begin(), ei = heapChunks.end(); i != ei; ++i) + i->memory.deallocate(); + } +}; + +#define SCRIBBLE(obj, c, size) \ + if (m_d->scribble) \ + ::memset((void *)(obj + 1), c, size - sizeof(Managed)); + + +namespace QV4 { + +bool operator<(const MemoryManager::Data::Chunk &a, const MemoryManager::Data::Chunk &b) +{ + return a.memory.base() < b.memory.base(); +} + +} // namespace QV4 + +MemoryManager::MemoryManager() + : m_d(new Data(true)) + , m_contextList(0) + , m_persistentValues(0) + , m_weakValues(0) +{ + setEnableGC(true); +#ifdef V4_USE_VALGRIND + VALGRIND_CREATE_MEMPOOL(this, 0, true); +#endif + +#if OS(QNX) + // TLS is at the top of each thread's stack, + // so the stack base for thread is the result of __tls() + m_d->stackTop = reinterpret_cast( + (((uintptr_t)__tls() + __PAGESIZE - 1) & ~(__PAGESIZE - 1))); +#elif USE(PTHREADS) +# if OS(DARWIN) + void *st = pthread_get_stackaddr_np(pthread_self()); + m_d->stackTop = static_cast(st); +# else + void* stackBottom = 0; + pthread_attr_t attr; + pthread_getattr_np(pthread_self(), &attr); + size_t stackSize = 0; + pthread_attr_getstack(&attr, &stackBottom, &stackSize); + pthread_attr_destroy(&attr); + + m_d->stackTop = static_cast(stackBottom) + stackSize/sizeof(quintptr); +# endif +#elif OS(WINDOWS) + PNT_TIB tib = (PNT_TIB)NtCurrentTeb(); + m_d->stackTop = static_cast(tib->StackBase); +#else +# error "Unsupported platform: no way to get the top-of-stack." +#endif + +} + +Managed *MemoryManager::alloc(std::size_t size) +{ + if (m_d->aggressiveGC) + runGC(); +#ifdef DETAILED_MM_STATS + willAllocate(size); +#endif // DETAILED_MM_STATS + + assert(size >= 16); + assert(size % 16 == 0); + + size_t pos = size >> 4; + ++m_d->allocCount[pos]; + + // fits into a small bucket + assert(size < MemoryManager::Data::MaxItemSize); + + Managed *m = m_d->smallItems[pos]; + if (m) + goto found; + + // try to free up space, otherwise allocate + if (m_d->allocCount[pos] > (m_d->availableItems[pos] >> 1) && !m_d->aggressiveGC) { + runGC(); + m = m_d->smallItems[pos]; + if (m) + goto found; + } + + // no free item available, allocate a new chunk + { + // allocate larger chunks at a time to avoid excessive GC, but cap at 64M chunks + uint shift = ++m_d->nChunks[pos]; + if (shift > 10) + shift = 10; + std::size_t allocSize = CHUNK_SIZE*(1 << shift); + allocSize = roundUpToMultipleOf(WTF::pageSize(), allocSize); + Data::Chunk allocation; + allocation.memory = PageAllocation::allocate(allocSize, OSAllocator::JSGCHeapPages); + allocation.chunkSize = size; + m_d->heapChunks.append(allocation); + qSort(m_d->heapChunks); + char *chunk = (char *)allocation.memory.base(); + char *end = chunk + allocation.memory.size() - size; + memset(chunk, 0, allocation.memory.size()); + Managed **last = &m_d->smallItems[pos]; + while (chunk <= end) { + Managed *o = reinterpret_cast(chunk); + o->_data = 0; + *last = o; + last = o->nextFreeRef(); + chunk += size; + } + *last = 0; + m = m_d->smallItems[pos]; + m_d->availableItems[pos] += allocation.memory.size()/size - 1; +#ifdef V4_USE_VALGRIND + VALGRIND_MAKE_MEM_NOACCESS(allocation.memory, allocation.chunkSize); +#endif + } + + found: +#ifdef V4_USE_VALGRIND + VALGRIND_MEMPOOL_ALLOC(this, m, size); +#endif + + m_d->smallItems[pos] = m->nextFree(); + return m; +} + +void MemoryManager::mark() +{ + m_d->engine->markObjects(); + + for (QHash::const_iterator it = m_d->protectedObject.begin(); it != m_d->protectedObject.constEnd(); ++it) + it.key()->mark(); + + PersistentValuePrivate *persistent = m_persistentValues; + while (persistent) { + if (!persistent->refcount) { + PersistentValuePrivate *n = persistent->next; + persistent->removeFromList(); + delete persistent; + persistent = n; + continue; + } + if (Managed *m = persistent->value.asManaged()) + m->mark(); + persistent = persistent->next; + } + + // push all caller saved registers to the stack, so we can find the objects living in these registers +#if COMPILER(MSVC) +# if CPU(X86_64) + HANDLE thread = GetCurrentThread(); + WOW64_CONTEXT ctxt; + /*bool success =*/ Wow64GetThreadContext(thread, &ctxt); +# elif CPU(X86) + HANDLE thread = GetCurrentThread(); + CONTEXT ctxt; + /*bool success =*/ GetThreadContext(thread, &ctxt); +# endif // CPU +#elif COMPILER(CLANG) || COMPILER(GCC) +# if CPU(X86_64) + quintptr regs[5]; + asm( + "mov %%rbp, %0\n" + "mov %%r12, %1\n" + "mov %%r13, %2\n" + "mov %%r14, %3\n" + "mov %%r15, %4\n" + : "=m" (regs[0]), "=m" (regs[1]), "=m" (regs[2]), "=m" (regs[3]), "=m" (regs[4]) + : + : + ); +# endif // CPU +#endif // COMPILER + + collectFromStack(); + + // Preserve QObject ownership rules within JavaScript: A parent with c++ ownership + // keeps all of its children alive in JavaScript. + + // Do this _after_ collectFromStack to ensure that processing the weak + // managed objects in the loop down there doesn't make then end up as leftovers + // on the stack and thus always get collected. + for (PersistentValuePrivate *weak = m_weakValues; weak; weak = weak->next) { + if (!weak->refcount) + continue; + QObjectWrapper *qobjectWrapper = weak->value.as(); + if (!qobjectWrapper) + continue; + QObject *qobject = qobjectWrapper->object(); + if (!qobject) + continue; + bool keepAlive = QQmlData::keepAliveDuringGarbageCollection(qobject); + + if (!keepAlive) { + if (QObject *parent = qobject->parent()) { + while (parent->parent()) + parent = parent->parent(); + + keepAlive = QQmlData::keepAliveDuringGarbageCollection(parent); + } + } + + if (keepAlive) + qobjectWrapper->mark(); + } +} + +std::size_t MemoryManager::sweep(bool lastSweep) +{ + PersistentValuePrivate *weak = m_weakValues; + while (weak) { + if (!weak->refcount) { + PersistentValuePrivate *n = weak->next; + weak->removeFromList(); + delete weak; + weak = n; + continue; + } + if (Managed *m = weak->value.asManaged()) { + if (!m->markBit) { + weak->value = Value::emptyValue(); + PersistentValuePrivate *n = weak->next; + weak->removeFromList(); + weak = n; + continue; + } + } + weak = weak->next; + } + + if (MultiplyWrappedQObjectMap *multiplyWrappedQObjects = m_d->engine->m_multiplyWrappedQObjects) { + for (MultiplyWrappedQObjectMap::Iterator it = multiplyWrappedQObjects->begin(); it != multiplyWrappedQObjects->end();) { + if (!it.value()->markBit) + it = multiplyWrappedQObjects->erase(it); + else + ++it; + } + } + + std::size_t freedCount = 0; + GCDeletable *deletable = 0; + GCDeletable **firstDeletable = &deletable; + + for (QVector::iterator i = m_d->heapChunks.begin(), ei = m_d->heapChunks.end(); i != ei; ++i) + freedCount += sweep(reinterpret_cast(i->memory.base()), i->memory.size(), i->chunkSize, &deletable); + + ExecutionContext *ctx = m_contextList; + ExecutionContext **n = &m_contextList; + while (ctx) { + ExecutionContext *next = ctx->next; + if (!ctx->marked) { + free(ctx); + *n = next; + } else { + ctx->marked = false; + n = &ctx->next; + } + ctx = next; + } + + deletable = *firstDeletable; + while (deletable) { + GCDeletable *next = deletable->next; + deletable->lastCall = lastSweep; + delete deletable; + deletable = next; + } + + return freedCount; +} + +std::size_t MemoryManager::sweep(char *chunkStart, std::size_t chunkSize, size_t size, GCDeletable **deletable) +{ +// qDebug("chunkStart @ %p, size=%x, pos=%x (%x)", chunkStart, size, size>>4, m_d->smallItems[size >> 4]); + std::size_t freedCount = 0; + + Managed **f = &m_d->smallItems[size >> 4]; + +#ifdef V4_USE_VALGRIND + VALGRIND_DISABLE_ERROR_REPORTING; +#endif + for (char *chunk = chunkStart, *chunkEnd = chunk + chunkSize - size; chunk <= chunkEnd; chunk += size) { + Managed *m = reinterpret_cast(chunk); +// qDebug("chunk @ %p, size = %lu, in use: %s, mark bit: %s", +// chunk, m->size, (m->inUse ? "yes" : "no"), (m->markBit ? "true" : "false")); + + assert((intptr_t) chunk % 16 == 0); + + if (m->inUse) { + if (m->markBit) { + m->markBit = 0; + } else { +// qDebug() << "-- collecting it." << m << *f << m->nextFree(); +#ifdef V4_USE_VALGRIND + VALGRIND_ENABLE_ERROR_REPORTING; +#endif + if (m->vtbl->collectDeletables) + m->vtbl->collectDeletables(m, deletable); + m->vtbl->destroy(m); + + m->setNextFree(*f); +#ifdef V4_USE_VALGRIND + VALGRIND_DISABLE_ERROR_REPORTING; + VALGRIND_MEMPOOL_FREE(this, m); +#endif + *f = m; + SCRIBBLE(m, 0x99, size); + ++freedCount; + } + } + } +#ifdef V4_USE_VALGRIND + VALGRIND_ENABLE_ERROR_REPORTING; +#endif + + return freedCount; +} + +bool MemoryManager::isGCBlocked() const +{ + return m_d->gcBlocked; +} + +void MemoryManager::setGCBlocked(bool blockGC) +{ + m_d->gcBlocked = blockGC; +} + +void MemoryManager::runGC() +{ + if (!m_d->enableGC || m_d->gcBlocked) { +// qDebug() << "Not running GC."; + return; + } + +// QTime t; t.start(); + +// qDebug() << ">>>>>>>>runGC"; + + mark(); +// std::cerr << "GC: marked " << marks +// << " objects in " << t.elapsed() +// << "ms" << std::endl; + +// t.restart(); + /*std::size_t freedCount =*/ sweep(); +// std::cerr << "GC: sweep freed " << freedCount +// << " objects in " << t.elapsed() +// << "ms" << std::endl; + memset(m_d->allocCount, 0, sizeof(m_d->allocCount)); +} + +void MemoryManager::setEnableGC(bool enableGC) +{ + m_d->enableGC = enableGC; +} + +MemoryManager::~MemoryManager() +{ + PersistentValuePrivate *persistent = m_persistentValues; + while (persistent) { + PersistentValuePrivate *n = persistent->next; + persistent->value = Value::undefinedValue(); + persistent->engine = 0; + persistent->prev = 0; + persistent->next = 0; + persistent = n; + } + + sweep(/*lastSweep*/true); +#ifdef V4_USE_VALGRIND + VALGRIND_DESTROY_MEMPOOL(this); +#endif +} + +void MemoryManager::protect(Managed *m) +{ + ++m_d->protectedObject[m]; +} + +void MemoryManager::unprotect(Managed *m) +{ + if (!--m_d->protectedObject[m]) + m_d->protectedObject.remove(m); +} + +static inline void add(QVector &values, const Value &v) +{ + if (Object *o = v.asObject()) + values.append(o); +} + +void MemoryManager::setExecutionEngine(ExecutionEngine *engine) +{ + m_d->engine = engine; +} + +void MemoryManager::dumpStats() const +{ +#ifdef DETAILED_MM_STATS + std::cerr << "=================" << std::endl; + std::cerr << "Allocation stats:" << std::endl; + std::cerr << "Requests for each chunk size:" << std::endl; + for (int i = 0; i < m_d->allocSizeCounters.size(); ++i) { + if (unsigned count = m_d->allocSizeCounters[i]) { + std::cerr << "\t" << (i << 4) << " bytes chunks: " << count << std::endl; + } + } +#endif // DETAILED_MM_STATS +} + +ExecutionEngine *MemoryManager::engine() const +{ + return m_d->engine; +} + +#ifdef DETAILED_MM_STATS +void MemoryManager::willAllocate(std::size_t size) +{ + unsigned alignedSize = (size + 15) >> 4; + QVector &counters = m_d->allocSizeCounters; + if ((unsigned) counters.size() < alignedSize + 1) + counters.resize(alignedSize + 1); + counters[alignedSize]++; +} + +#endif // DETAILED_MM_STATS + +void MemoryManager::collectFromStack() const +{ + quintptr valueOnStack = 0; + + if (!m_d->heapChunks.count()) + return; + + quintptr *current = (&valueOnStack) + 1; +// qDebug() << "collectFromStack";// << top << current << &valueOnStack; + +#if V4_USE_VALGRIND + VALGRIND_MAKE_MEM_DEFINED(current, (m_d->stackTop - current)*sizeof(quintptr)); +#endif + + char** heapChunkBoundaries = (char**)alloca(m_d->heapChunks.count() * 2 * sizeof(char*)); + char** heapChunkBoundariesEnd = heapChunkBoundaries + 2 * m_d->heapChunks.count(); + int i = 0; + for (QVector::Iterator it = m_d->heapChunks.begin(), end = + m_d->heapChunks.end(); it != end; ++it) { + heapChunkBoundaries[i++] = reinterpret_cast(it->memory.base()) - 1; + heapChunkBoundaries[i++] = reinterpret_cast(it->memory.base()) + it->memory.size() - it->chunkSize; + } + assert(i == m_d->heapChunks.count() * 2); + + for (; current < m_d->stackTop; ++current) { + char* genericPtr = +#if QT_POINTER_SIZE == 8 + reinterpret_cast((*current) & ~(quint64(Value::Type_Mask) << Value::Tag_Shift)); +#else + reinterpret_cast(*current); +#endif + + if (genericPtr < *heapChunkBoundaries || genericPtr > *(heapChunkBoundariesEnd - 1)) + continue; + int index = qLowerBound(heapChunkBoundaries, heapChunkBoundariesEnd, genericPtr) - heapChunkBoundaries; + // An odd index means the pointer is _before_ the end of a heap chunk and therefore valid. + assert(index >= 0 && index < m_d->heapChunks.count() * 2); + if (index & 1) { + int size = m_d->heapChunks.at(index >> 1).chunkSize; + Managed *m = reinterpret_cast(genericPtr); +// qDebug() << " inside" << size; + + if (((quintptr)m - (quintptr)heapChunkBoundaries[index-1] - 1 ) % size) + // wrongly aligned value, skip it + continue; + + if (!m->inUse) + // Skip pointers to already freed objects, they are bogus as well + continue; + +// qDebug() << " marking"; + m->mark(); + } + } +} + +QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4mm_p.h b/src/qml/jsruntime/qv4mm_p.h new file mode 100644 index 0000000000..f72d23dc9a --- /dev/null +++ b/src/qml/jsruntime/qv4mm_p.h @@ -0,0 +1,157 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4GC_H +#define QV4GC_H + +#include "qv4global_p.h" +#include "qv4context_p.h" + +#include + +//#define DETAILED_MM_STATS + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct ExecutionEngine; +struct ExecutionContext; +struct Managed; +struct GCDeletable; + +class Q_QML_EXPORT MemoryManager +{ + MemoryManager(const MemoryManager &); + MemoryManager &operator=(const MemoryManager&); + +public: + struct Data; + + class GCBlocker + { + public: + GCBlocker(MemoryManager *mm) + : mm(mm) + , wasBlocked(mm->isGCBlocked()) + { + mm->setGCBlocked(true); + } + + ~GCBlocker() + { + mm->setGCBlocked(wasBlocked); + } + + private: + MemoryManager *mm; + bool wasBlocked; + }; + +public: + MemoryManager(); + ~MemoryManager(); + + void protect(Managed *m); + void unprotect(Managed *m); + + // TODO: this is only for 64bit (and x86 with SSE/AVX), so exend it for other architectures to be slightly more efficient (meaning, align on 8-byte boundaries). + // Note: all occurances of "16" in alloc/dealloc are also due to the alignment. + static inline std::size_t align(std::size_t size) + { return (size + 15) & ~0xf; } + + inline Managed *allocManaged(std::size_t size) + { + size = align(size); + Managed *o = alloc(size); + return o; + } + + ExecutionContext *allocContext(uint size); + + bool isGCBlocked() const; + void setGCBlocked(bool blockGC); + void runGC(); + + void setEnableGC(bool enableGC); + void setExecutionEngine(ExecutionEngine *engine); + + void dumpStats() const; + +protected: + /// expects size to be aligned + // TODO: try to inline + Managed *alloc(std::size_t size); + + ExecutionEngine *engine() const; + +#ifdef DETAILED_MM_STATS + void willAllocate(std::size_t size); +#endif // DETAILED_MM_STATS + +private: + void collectFromStack() const; + void mark(); + std::size_t sweep(bool lastSweep = false); + std::size_t sweep(char *chunkStart, std::size_t chunkSize, size_t size, GCDeletable **deletable); + +protected: + QScopedPointer m_d; + ExecutionContext *m_contextList; +public: + PersistentValuePrivate *m_persistentValues; + PersistentValuePrivate *m_weakValues; +}; + +inline ExecutionContext *MemoryManager::allocContext(uint size) +{ + ExecutionContext *newContext = (ExecutionContext *)malloc(size); + newContext->next = m_contextList; + m_contextList = newContext; + return newContext; +} + + +} + +QT_END_NAMESPACE + +#endif // QV4GC_H diff --git a/src/qml/jsruntime/qv4numberobject.cpp b/src/qml/jsruntime/qv4numberobject.cpp new file mode 100644 index 0000000000..266fa792dc --- /dev/null +++ b/src/qml/jsruntime/qv4numberobject.cpp @@ -0,0 +1,257 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4numberobject_p.h" +#include +#include +#include +#include +#include + +using namespace QV4; + +DEFINE_MANAGED_VTABLE(NumberCtor); + +NumberCtor::NumberCtor(ExecutionContext *scope) + : FunctionObject(scope, scope->engine->newIdentifier(QStringLiteral("Number"))) +{ + vtbl = &static_vtbl; +} + +Value NumberCtor::construct(Managed *m, Value *args, int argc) +{ + double d = argc ? args[0].toNumber() : 0.; + return Value::fromObject(m->engine()->newNumberObject(Value::fromDouble(d))); +} + +Value NumberCtor::call(Managed *, const Value &, Value *argv, int argc) +{ + double d = argc ? argv[0].toNumber() : 0.; + return Value::fromDouble(d); +} + +void NumberPrototype::init(ExecutionContext *ctx, const Value &ctor) +{ + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); + + ctor.objectValue()->defineReadonlyProperty(ctx->engine, QStringLiteral("NaN"), Value::fromDouble(qSNaN())); + ctor.objectValue()->defineReadonlyProperty(ctx->engine, QStringLiteral("NEGATIVE_INFINITY"), Value::fromDouble(-qInf())); + ctor.objectValue()->defineReadonlyProperty(ctx->engine, QStringLiteral("POSITIVE_INFINITY"), Value::fromDouble(qInf())); + ctor.objectValue()->defineReadonlyProperty(ctx->engine, QStringLiteral("MAX_VALUE"), Value::fromDouble(1.7976931348623158e+308)); + +#ifdef __INTEL_COMPILER +# pragma warning( push ) +# pragma warning(disable: 239) +#endif + ctor.objectValue()->defineReadonlyProperty(ctx->engine, QStringLiteral("MIN_VALUE"), Value::fromDouble(5e-324)); +#ifdef __INTEL_COMPILER +# pragma warning( pop ) +#endif + + defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); + defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString); + defineDefaultProperty(ctx, QStringLiteral("toLocaleString"), method_toLocaleString); + defineDefaultProperty(ctx, QStringLiteral("valueOf"), method_valueOf); + defineDefaultProperty(ctx, QStringLiteral("toFixed"), method_toFixed, 1); + defineDefaultProperty(ctx, QStringLiteral("toExponential"), method_toExponential); + defineDefaultProperty(ctx, QStringLiteral("toPrecision"), method_toPrecision); +} + +Value NumberPrototype::method_toString(SimpleCallContext *ctx) +{ + double num; + if (ctx->thisObject.isNumber()) { + num = ctx->thisObject.asDouble(); + } else { + NumberObject *thisObject = ctx->thisObject.asNumberObject(); + if (!thisObject) + ctx->throwTypeError(); + num = thisObject->value.asDouble(); + } + + Value arg = ctx->argument(0); + if (!arg.isUndefined()) { + int radix = arg.toInt32(); + if (radix < 2 || radix > 36) { + ctx->throwError(QString::fromLatin1("Number.prototype.toString: %0 is not a valid radix") + .arg(radix)); + return Value::undefinedValue(); + } + + if (std::isnan(num)) { + return Value::fromString(ctx, QStringLiteral("NaN")); + } else if (qIsInf(num)) { + return Value::fromString(ctx, QLatin1String(num < 0 ? "-Infinity" : "Infinity")); + } + + if (radix != 10) { + QString str; + bool negative = false; + if (num < 0) { + negative = true; + num = -num; + } + double frac = num - ::floor(num); + num = Value::toInteger(num); + do { + char c = (char)::fmod(num, radix); + c = (c < 10) ? (c + '0') : (c - 10 + 'a'); + str.prepend(QLatin1Char(c)); + num = ::floor(num / radix); + } while (num != 0); + if (frac != 0) { + str.append(QLatin1Char('.')); + do { + frac = frac * radix; + char c = (char)::floor(frac); + c = (c < 10) ? (c + '0') : (c - 10 + 'a'); + str.append(QLatin1Char(c)); + frac = frac - ::floor(frac); + } while (frac != 0); + } + if (negative) + str.prepend(QLatin1Char('-')); + return Value::fromString(ctx, str); + } + } + + String *str = Value::fromDouble(num).toString(ctx); + return Value::fromString(str); +} + +Value NumberPrototype::method_toLocaleString(SimpleCallContext *ctx) +{ + NumberObject *thisObject = ctx->thisObject.asNumberObject(); + if (!thisObject) + ctx->throwTypeError(); + + String *str = thisObject->value.toString(ctx); + return Value::fromString(str); +} + +Value NumberPrototype::method_valueOf(SimpleCallContext *ctx) +{ + NumberObject *thisObject = ctx->thisObject.asNumberObject(); + if (!thisObject) + ctx->throwTypeError(); + + return thisObject->value; +} + +Value NumberPrototype::method_toFixed(SimpleCallContext *ctx) +{ + NumberObject *thisObject = ctx->thisObject.asNumberObject(); + if (!thisObject) + ctx->throwTypeError(); + + double fdigits = 0; + + if (ctx->argumentCount > 0) + fdigits = ctx->argument(0).toInteger(); + + if (std::isnan(fdigits)) + fdigits = 0; + + if (fdigits < 0 || fdigits > 20) + ctx->throwRangeError(ctx->thisObject); + + double v = thisObject->value.asDouble(); + QString str; + if (std::isnan(v)) + str = QString::fromLatin1("NaN"); + else if (qIsInf(v)) + str = QString::fromLatin1(v < 0 ? "-Infinity" : "Infinity"); + else if (v < 1.e21) + str = QString::number(v, 'f', int (fdigits)); + else + return __qmljs_string_from_number(ctx, v); + return Value::fromString(ctx, str); +} + +Value NumberPrototype::method_toExponential(SimpleCallContext *ctx) +{ + NumberObject *thisObject = ctx->thisObject.asNumberObject(); + if (!thisObject) + ctx->throwTypeError(); + + Value fraction = ctx->argument(0); + int fdigits = -1; + + if (!fraction.isUndefined()) { + int fdigits = ctx->argument(0).toInt32(); + if (fdigits < 0 || fdigits > 20) { + String *error = ctx->engine->newString(QStringLiteral("Number.prototype.toExponential: fractionDigits out of range")); + ctx->throwRangeError(Value::fromString(error)); + } + } + + char str[100]; + double_conversion::StringBuilder builder(str, sizeof(str)); + double_conversion::DoubleToStringConverter::EcmaScriptConverter().ToExponential(thisObject->value.asDouble(), fdigits, &builder); + QString result = QString::fromLatin1(builder.Finalize()); + + return Value::fromString(ctx, result); +} + +Value NumberPrototype::method_toPrecision(SimpleCallContext *ctx) +{ + NumberObject *thisObject = ctx->thisObject.asNumberObject(); + if (!thisObject) + ctx->throwTypeError(); + + Value prec = ctx->argument(0); + if (prec.isUndefined()) + return __qmljs_to_string(thisObject->value, ctx); + + double precision = prec.toInt32(); + if (precision < 1 || precision > 21) { + String *error = ctx->engine->newString(QStringLiteral("Number.prototype.toPrecision: precision out of range")); + ctx->throwRangeError(Value::fromString(error)); + } + + char str[100]; + double_conversion::StringBuilder builder(str, sizeof(str)); + double_conversion::DoubleToStringConverter::EcmaScriptConverter().ToPrecision(thisObject->value.asDouble(), precision, &builder); + QString result = QString::fromLatin1(builder.Finalize()); + + return Value::fromString(ctx, result); +} diff --git a/src/qml/jsruntime/qv4numberobject_p.h b/src/qml/jsruntime/qv4numberobject_p.h new file mode 100644 index 0000000000..0c06451c98 --- /dev/null +++ b/src/qml/jsruntime/qv4numberobject_p.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4NUMBEROBJECT_H +#define QV4NUMBEROBJECT_H + +#include "qv4object_p.h" +#include "qv4functionobject_p.h" +#include + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct NumberCtor: FunctionObject +{ + NumberCtor(ExecutionContext *scope); + + static Value construct(Managed *that, Value *args, int argc); + static Value call(Managed *, const Value &, Value *, int); + +protected: + static const ManagedVTable static_vtbl; +}; + +struct NumberPrototype: NumberObject +{ + NumberPrototype(ExecutionEngine *engine): NumberObject(engine, Value::fromDouble(0)) {} + void init(ExecutionContext *ctx, const Value &ctor); + + static Value method_toString(SimpleCallContext *ctx); + static Value method_toLocaleString(SimpleCallContext *ctx); + static Value method_valueOf(SimpleCallContext *ctx); + static Value method_toFixed(SimpleCallContext *ctx); + static Value method_toExponential(SimpleCallContext *ctx); + static Value method_toPrecision(SimpleCallContext *ctx); +}; + + +} + +QT_END_NAMESPACE + +#endif // QV4ECMAOBJECTS_P_H diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp new file mode 100644 index 0000000000..edfb535e7a --- /dev/null +++ b/src/qml/jsruntime/qv4object.cpp @@ -0,0 +1,1407 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4object_p.h" +#include "qv4jsir_p.h" +#include "qv4isel_p.h" +#include "qv4objectproto_p.h" +#include "qv4stringobject_p.h" +#include "qv4argumentsobject_p.h" +#include "qv4mm_p.h" +#include "qv4lookup_p.h" + +#include +#include +#include +#include +#include +#include +#include "private/qlocale_tools_p.h" + +#include +#include +#include +#include +#include +#include +#include "qv4alloca_p.h" + +using namespace QV4; + +DEFINE_MANAGED_VTABLE(Object); + +Object::Object(ExecutionEngine *engine) + : Managed(engine->emptyClass) + , prototype(0) + , memberDataAlloc(InlinePropertySize), memberData(inlineProperties) + , arrayOffset(0), arrayDataLen(0), arrayAlloc(0), arrayAttributes(0), arrayData(0), sparseArray(0) +{ + vtbl = &static_vtbl; + type = Type_Object; + memset(memberData, 0, sizeof(Property)*memberDataAlloc); +} + +Object::Object(ExecutionContext *context) + : Managed(context->engine->emptyClass) + , prototype(0) + , memberDataAlloc(InlinePropertySize), memberData(inlineProperties) + , arrayOffset(0), arrayDataLen(0), arrayAlloc(0), arrayAttributes(0), arrayData(0), sparseArray(0) +{ + vtbl = &static_vtbl; + type = Type_Object; + memset(memberData, 0, sizeof(Property)*memberDataAlloc); +} + +Object::Object(ExecutionEngine *engine, InternalClass *internalClass) + : Managed(internalClass) + , prototype(0) + , memberDataAlloc(InlinePropertySize), memberData(inlineProperties) + , arrayOffset(0), arrayDataLen(0), arrayAlloc(0), arrayAttributes(0), arrayData(0), sparseArray(0) +{ + vtbl = &static_vtbl; + type = Type_Object; + + if (internalClass->size >= memberDataAlloc) { + memberDataAlloc = internalClass->size; + memberData = new Property[memberDataAlloc]; + } + memset(memberData, 0, sizeof(Property)*memberDataAlloc); +} + +Object::~Object() +{ + if (memberData != inlineProperties) + delete [] memberData; + delete [] (arrayData - (sparseArray ? 0 : arrayOffset)); + if (arrayAttributes) + delete [] (arrayAttributes - (sparseArray ? 0 : arrayOffset)); + delete sparseArray; + _data = 0; +} + +void Object::destroy(Managed *that) +{ + static_cast(that)->~Object(); +} + +void Object::put(ExecutionContext *ctx, const QString &name, const Value &value) +{ + put(ctx->engine->newString(name), value); +} + +Value Object::getValue(const Value &thisObject, const Property *p, PropertyAttributes attrs) +{ + if (!attrs.isAccessor()) + return p->value; + FunctionObject *getter = p->getter(); + if (!getter) + return Value::undefinedValue(); + + return getter->call(thisObject, 0, 0); +} + +void Object::putValue(Property *pd, PropertyAttributes attrs, const Value &value) +{ + if (attrs.isAccessor()) { + if (pd->set) { + Value args[1]; + args[0] = value; + pd->set->call(Value::fromObject(this), args, 1); + return; + } + goto reject; + } + + if (!attrs.isWritable()) + goto reject; + + pd->value = value; + return; + + reject: + if (engine()->current->strictMode) + engine()->current->throwTypeError(); + +} + +void Object::inplaceBinOp(ExecutionContext *, BinOp op, String *name, const Value &rhs) +{ + Value v = get(name); + Value result; + op(&result, v, rhs); + put(name, result); +} + +void Object::inplaceBinOp(ExecutionContext *ctx, BinOp op, const Value &index, const Value &rhs) +{ + uint idx = index.asArrayIndex(); + if (idx < UINT_MAX) { + bool hasProperty = false; + Value v = getIndexed(idx, &hasProperty); + Value result; + op(&result, v, rhs); + putIndexed(idx, result); + return; + } + String *name = index.toString(ctx); + assert(name); + inplaceBinOp(ctx, op, name, rhs); +} + +void Object::inplaceBinOp(ExecutionContext *ctx, BinOpContext op, String *name, const Value &rhs) +{ + Value v = get(name); + Value result; + op(ctx, &result, v, rhs); + put(name, result); +} + +void Object::inplaceBinOp(ExecutionContext *ctx, BinOpContext op, const Value &index, const Value &rhs) +{ + uint idx = index.asArrayIndex(); + if (idx < UINT_MAX) { + bool hasProperty = false; + Value v = getIndexed(idx, &hasProperty); + Value result; + op(ctx, &result, v, rhs); + putIndexed(idx, result); + return; + } + String *name = index.toString(ctx); + assert(name); + inplaceBinOp(ctx, op, name, rhs); +} + +void Object::defineDefaultProperty(String *name, Value value) +{ + Property *pd = insertMember(name, Attr_Data|Attr_NotEnumerable); + pd->value = value; +} + +void Object::defineDefaultProperty(ExecutionContext *context, const QString &name, Value value) +{ + defineDefaultProperty(context->engine->newIdentifier(name), value); +} + +void Object::defineDefaultProperty(ExecutionEngine *engine, const QString &name, Value value) +{ + defineDefaultProperty(engine->newIdentifier(name), value); +} + +void Object::defineDefaultProperty(ExecutionContext *context, const QString &name, Value (*code)(SimpleCallContext *), int argumentCount) +{ + Q_UNUSED(argumentCount); + String *s = context->engine->newIdentifier(name); + FunctionObject* function = context->engine->newBuiltinFunction(context, s, code); + function->defineReadonlyProperty(context->engine->id_length, Value::fromInt32(argumentCount)); + defineDefaultProperty(s, Value::fromObject(function)); +} + +void Object::defineDefaultProperty(ExecutionEngine *engine, const QString &name, Value (*code)(SimpleCallContext *), int argumentCount) +{ + Q_UNUSED(argumentCount); + String *s = engine->newIdentifier(name); + FunctionObject* function = engine->newBuiltinFunction(engine->rootContext, s, code); + function->defineReadonlyProperty(engine->id_length, Value::fromInt32(argumentCount)); + defineDefaultProperty(s, Value::fromObject(function)); +} + +void Object::defineAccessorProperty(ExecutionEngine *engine, const QString &name, + Value (*getter)(SimpleCallContext *), Value (*setter)(SimpleCallContext *)) +{ + String *s = engine->newString(name); + defineAccessorProperty(s, getter, setter); +} + +void Object::defineAccessorProperty(String *name, Value (*getter)(SimpleCallContext *), Value (*setter)(SimpleCallContext *)) +{ + ExecutionEngine *v4 = engine(); + Property *p = insertMember(name, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); + if (getter) + p->setGetter(v4->newBuiltinFunction(v4->rootContext, name, getter)); + if (setter) + p->setSetter(v4->newBuiltinFunction(v4->rootContext, name, setter)); +} + +void Object::defineReadonlyProperty(ExecutionEngine *engine, const QString &name, Value value) +{ + defineReadonlyProperty(engine->newIdentifier(name), value); +} + +void Object::defineReadonlyProperty(String *name, Value value) +{ + Property *pd = insertMember(name, Attr_ReadOnly); + pd->value = value; +} + +void Object::markObjects(Managed *that) +{ + Object *o = static_cast(that); + if (o->prototype) + o->prototype->mark(); + + for (int i = 0; i < o->internalClass->size; ++i) { + const Property &pd = o->memberData[i]; + if (o->internalClass->propertyData[i].isData()) { + if (Managed *m = pd.value.asManaged()) + m->mark(); + } else { + if (pd.getter()) + pd.getter()->mark(); + if (pd.setter()) + pd.setter()->mark(); + } + } + o->markArrayObjects(); +} + +Property *Object::insertMember(String *s, PropertyAttributes attributes) +{ + uint idx; + internalClass = internalClass->addMember(s, attributes, &idx); + + if (idx >= memberDataAlloc) { + memberDataAlloc = qMax((uint)8, 2*memberDataAlloc); + Property *newMemberData = new Property[memberDataAlloc]; + memcpy(newMemberData, memberData, sizeof(Property)*idx); + memset(newMemberData + idx, 0, sizeof(Property)*(memberDataAlloc - idx)); + if (memberData != inlineProperties) + delete [] memberData; + memberData = newMemberData; + } + return memberData + idx; +} + +// Section 8.12.1 +Property *Object::__getOwnProperty__(String *name, PropertyAttributes *attrs) +{ + uint idx = name->asArrayIndex(); + if (idx != UINT_MAX) + return __getOwnProperty__(idx, attrs); + + uint member = internalClass->find(name); + if (member < UINT_MAX) { + if (attrs) + *attrs = internalClass->propertyData[member]; + return memberData + member; + } + + if (attrs) + *attrs = Attr_Invalid; + return 0; +} + +Property *Object::__getOwnProperty__(uint index, PropertyAttributes *attrs) +{ + uint pidx = propertyIndexFromArrayIndex(index); + if (pidx < UINT_MAX) { + Property *p = arrayData + pidx; + if (!arrayAttributes || arrayAttributes[pidx].isData()) { + if (attrs) + *attrs = arrayAttributes ? arrayAttributes[pidx] : PropertyAttributes(Attr_Data); + return p; + } else if (arrayAttributes[pidx].isAccessor()) { + if (attrs) + *attrs = arrayAttributes ? arrayAttributes[pidx] : PropertyAttributes(Attr_Accessor); + return p; + } + } + if (isStringObject()) { + if (attrs) + *attrs = Attr_NotConfigurable|Attr_NotWritable; + return static_cast(this)->getIndex(index); + } + + if (attrs) + *attrs = Attr_Invalid; + return 0; +} + +// Section 8.12.2 +Property *Object::__getPropertyDescriptor__(String *name, PropertyAttributes *attrs) const +{ + uint idx = name->asArrayIndex(); + if (idx != UINT_MAX) + return __getPropertyDescriptor__(idx); + + + const Object *o = this; + while (o) { + uint idx = o->internalClass->find(name); + if (idx < UINT_MAX) { + if (attrs) + *attrs = o->internalClass->propertyData[idx]; + return o->memberData + idx; + } + + o = o->prototype; + } + if (attrs) + *attrs = Attr_Invalid; + return 0; +} + +Property *Object::__getPropertyDescriptor__(uint index, PropertyAttributes *attrs) const +{ + const Object *o = this; + while (o) { + uint pidx = o->propertyIndexFromArrayIndex(index); + if (pidx < UINT_MAX) { + Property *p = o->arrayData + pidx; + if (!o->arrayAttributes || !o->arrayAttributes[pidx].isGeneric()) { + if (attrs) + *attrs = o->arrayAttributes ? o->arrayAttributes[pidx] : PropertyAttributes(Attr_Data); + return p; + } + } + if (o->isStringObject()) { + Property *p = static_cast(o)->getIndex(index); + if (p) { + if (attrs) + *attrs = (Attr_NotWritable|Attr_NotConfigurable); + return p; + } + } + o = o->prototype; + } + if (attrs) + *attrs = Attr_Invalid; + return 0; +} + +bool Object::__hasProperty__(String *name) const +{ + if (__getPropertyDescriptor__(name)) + return true; + return !query(name).isEmpty(); +} + +Value Object::get(Managed *m, String *name, bool *hasProperty) +{ + return static_cast(m)->internalGet(name, hasProperty); +} + +Value Object::getIndexed(Managed *m, uint index, bool *hasProperty) +{ + return static_cast(m)->internalGetIndexed(index, hasProperty); +} + +void Object::put(Managed *m, String *name, const Value &value) +{ + static_cast(m)->internalPut(name, value); +} + +void Object::putIndexed(Managed *m, uint index, const Value &value) +{ + static_cast(m)->internalPutIndexed(index, value); +} + +PropertyAttributes Object::query(const Managed *m, String *name) +{ + uint idx = name->asArrayIndex(); + if (idx != UINT_MAX) + return queryIndexed(m, idx); + + const Object *o = static_cast(m); + while (o) { + uint idx = o->internalClass->find(name); + if (idx < UINT_MAX) + return o->internalClass->propertyData[idx]; + + o = o->prototype; + } + return Attr_Invalid; +} + +PropertyAttributes Object::queryIndexed(const Managed *m, uint index) +{ + const Object *o = static_cast(m); + while (o) { + uint pidx = o->propertyIndexFromArrayIndex(index); + if (pidx < UINT_MAX) { + if (o->arrayAttributes) + return o->arrayAttributes[pidx]; + return Attr_Data; + } + if (o->isStringObject()) { + Property *p = static_cast(o)->getIndex(index); + if (p) + return Attr_Data; + } + o = o->prototype; + } + return Attr_Invalid; +} + +bool Object::deleteProperty(Managed *m, String *name) +{ + return static_cast(m)->internalDeleteProperty(name); +} + +bool Object::deleteIndexedProperty(Managed *m, uint index) +{ + return static_cast(m)->internalDeleteIndexedProperty(index); +} + +void Object::getLookup(Managed *m, Lookup *l, Value *result) +{ + Object *o = static_cast(m); + PropertyAttributes attrs; + Property *p = l->lookup(o, &attrs); + if (p) { + if (attrs.isData()) { + if (l->level == 0) + l->getter = Lookup::getter0; + else if (l->level == 1) + l->getter = Lookup::getter1; + else if (l->level == 2) + l->getter = Lookup::getter2; + if (result) + *result = p->value; + return; + } else { + if (l->level == 0) + l->getter = Lookup::getterAccessor0; + else if (l->level == 1) + l->getter = Lookup::getterAccessor1; + else if (l->level == 2) + l->getter = Lookup::getterAccessor2; + if (result) + *result = p->value; + Value res = o->getValue(p, attrs); + if (result) + *result = res; + return; + } + } else if (result) { + *result = Value::undefinedValue(); + } +} + +void Object::setLookup(Managed *m, Lookup *l, const Value &value) +{ + Object *o = static_cast(m); + + uint idx = o->internalClass->find(l->name); + if (!o->isArrayObject() || idx != ArrayObject::LengthPropertyIndex) { + if (idx != UINT_MAX && o->internalClass->propertyData[idx].isData() && o->internalClass->propertyData[idx].isWritable()) { + l->classList[0] = o->internalClass; + l->index = idx; + l->setter = Lookup::setter0; + o->memberData[idx].value = value; + return; + } + + if (idx != UINT_MAX) { + o->putValue(o->memberData + idx, o->internalClass->propertyData[idx], value); + return; + } + } + + o->put(l->name, value); +} + +Property *Object::advanceIterator(Managed *m, ObjectIterator *it, String **name, uint *index, PropertyAttributes *attrs) +{ + Object *o = static_cast(m); + *name = 0; + *index = UINT_MAX; + + if (!it->arrayIndex) + it->arrayNode = o->sparseArrayBegin(); + + // sparse arrays + if (it->arrayNode) { + while (it->arrayNode != o->sparseArrayEnd()) { + int k = it->arrayNode->key(); + uint pidx = it->arrayNode->value; + Property *p = o->arrayData + pidx; + it->arrayNode = it->arrayNode->nextNode(); + PropertyAttributes a = o->arrayAttributes ? o->arrayAttributes[pidx] : PropertyAttributes(Attr_Data); + if (!(it->flags & ObjectIterator::EnumerableOnly) || a.isEnumerable()) { + it->arrayIndex = k + 1; + *index = k; + if (attrs) + *attrs = a; + return p; + } + } + it->arrayNode = 0; + it->arrayIndex = UINT_MAX; + } + // dense arrays + while (it->arrayIndex < o->arrayDataLen) { + uint pidx = o->propertyIndexFromArrayIndex(it->arrayIndex); + Property *p = o->arrayData + pidx; + PropertyAttributes a = o->arrayAttributes ? o->arrayAttributes[pidx] : PropertyAttributes(Attr_Data); + ++it->arrayIndex; + if ((!o->arrayAttributes || !o->arrayAttributes[pidx].isGeneric()) + && (!(it->flags & ObjectIterator::EnumerableOnly) || a.isEnumerable())) { + *index = it->arrayIndex - 1; + if (attrs) + *attrs = a; + return p; + } + } + + while (it->memberIndex < o->internalClass->size) { + String *n = o->internalClass->nameMap.at(it->memberIndex); + assert(n); + + Property *p = o->memberData + it->memberIndex; + PropertyAttributes a = o->internalClass->propertyData[it->memberIndex]; + ++it->memberIndex; + if (!(it->flags & ObjectIterator::EnumerableOnly) || a.isEnumerable()) { + *name = n; + if (attrs) + *attrs = a; + return p; + } + } + + return 0; +} + +// Section 8.12.3 +Value Object::internalGet(String *name, bool *hasProperty) +{ + uint idx = name->asArrayIndex(); + if (idx != UINT_MAX) + return getIndexed(idx, hasProperty); + + name->makeIdentifier(); + + Object *o = this; + while (o) { + uint idx = o->internalClass->find(name); + if (idx < UINT_MAX) { + if (hasProperty) + *hasProperty = true; + return getValue(o->memberData + idx, o->internalClass->propertyData.at(idx)); + } + + o = o->prototype; + } + + if (hasProperty) + *hasProperty = false; + return Value::undefinedValue(); +} + +Value Object::internalGetIndexed(uint index, bool *hasProperty) +{ + Property *pd = 0; + PropertyAttributes attrs = Attr_Data; + Object *o = this; + while (o) { + uint pidx = o->propertyIndexFromArrayIndex(index); + if (pidx < UINT_MAX) { + if (!o->arrayAttributes || !o->arrayAttributes[pidx].isGeneric()) { + pd = o->arrayData + pidx; + if (o->arrayAttributes) + attrs = o->arrayAttributes[pidx]; + break; + } + } + if (o->isStringObject()) { + pd = static_cast(o)->getIndex(index); + if (pd) { + attrs = (Attr_NotWritable|Attr_NotConfigurable); + break; + } + } + o = o->prototype; + } + + if (pd) { + if (hasProperty) + *hasProperty = true; + return getValue(pd, attrs); + } + + if (hasProperty) + *hasProperty = false; + return Value::undefinedValue(); +} + + +// Section 8.12.5 +void Object::internalPut(String *name, const Value &value) +{ + uint idx = name->asArrayIndex(); + if (idx != UINT_MAX) + return putIndexed(idx, value); + + name->makeIdentifier(); + + uint member = internalClass->find(name); + Property *pd = 0; + PropertyAttributes attrs; + if (member < UINT_MAX) { + pd = memberData + member; + attrs = internalClass->propertyData[member]; + } + + // clause 1 + if (pd) { + if (attrs.isAccessor()) { + if (pd->setter()) + goto cont; + goto reject; + } else if (!attrs.isWritable()) + goto reject; + else if (isArrayObject() && name->isEqualTo(engine()->id_length)) { + bool ok; + uint l = value.asArrayLength(&ok); + if (!ok) + engine()->current->throwRangeError(value); + ok = setArrayLength(l); + if (!ok) + goto reject; + } else { + pd->value = value; + } + return; + } else if (!prototype) { + if (!extensible) + goto reject; + } else { + // clause 4 + if ((pd = prototype->__getPropertyDescriptor__(name, &attrs))) { + if (attrs.isAccessor()) { + if (!pd->setter()) + goto reject; + } else if (!extensible || !attrs.isWritable()) { + goto reject; + } + } else if (!extensible) { + goto reject; + } + } + + cont: + + // Clause 5 + if (pd && attrs.isAccessor()) { + assert(pd->setter() != 0); + + Value args[1]; + args[0] = value; + pd->setter()->call(Value::fromObject(this), args, 1); + return; + } + + { + Property *p = insertMember(name, Attr_Data); + p->value = value; + return; + } + + reject: + if (engine()->current->strictMode) { + QString message = QStringLiteral("Cannot assign to read-only property \""); + message += name->toQString(); + message += QLatin1Char('\"'); + engine()->current->throwTypeError(message); + } +} + +void Object::internalPutIndexed(uint index, const Value &value) +{ + Property *pd = 0; + PropertyAttributes attrs; + + uint pidx = propertyIndexFromArrayIndex(index); + if (pidx < UINT_MAX) { + if (arrayAttributes && arrayAttributes[pidx].isGeneric()) { + pidx = UINT_MAX; + } else { + pd = arrayData + pidx; + attrs = arrayAttributes ? arrayAttributes[pidx] : PropertyAttributes(Attr_Data); + } + } + + if (!pd && isStringObject()) { + pd = static_cast(this)->getIndex(index); + if (pd) + // not writable + goto reject; + } + + // clause 1 + if (pd) { + if (attrs.isAccessor()) { + if (pd->setter()) + goto cont; + goto reject; + } else if (!attrs.isWritable()) + goto reject; + else + pd->value = value; + return; + } else if (!prototype) { + if (!extensible) + goto reject; + } else { + // clause 4 + if ((pd = prototype->__getPropertyDescriptor__(index, &attrs))) { + if (attrs.isAccessor()) { + if (!pd->setter()) + goto reject; + } else if (!extensible || !attrs.isWritable()) { + goto reject; + } + } else if (!extensible) { + goto reject; + } + } + + cont: + + // Clause 5 + if (pd && attrs.isAccessor()) { + assert(pd->setter() != 0); + + Value args[1]; + args[0] = value; + pd->setter()->call(Value::fromObject(this), args, 1); + return; + } + + arraySet(index, value); + return; + + reject: + if (engine()->current->strictMode) + engine()->current->throwTypeError(); +} + +// Section 8.12.7 +bool Object::internalDeleteProperty(String *name) +{ + uint idx = name->asArrayIndex(); + if (idx != UINT_MAX) + return deleteIndexedProperty(idx); + + name->makeIdentifier(); + + uint memberIdx = internalClass->find(name); + if (memberIdx != UINT_MAX) { + if (internalClass->propertyData[memberIdx].isConfigurable()) { + internalClass->removeMember(this, name->identifier); + memmove(memberData + memberIdx, memberData + memberIdx + 1, (internalClass->size - memberIdx)*sizeof(Property)); + return true; + } + if (engine()->current->strictMode) + engine()->current->throwTypeError(); + return false; + } + + return true; +} + +bool Object::internalDeleteIndexedProperty(uint index) +{ + uint pidx = propertyIndexFromArrayIndex(index); + if (pidx == UINT_MAX) + return true; + if (arrayAttributes && arrayAttributes[pidx].isGeneric()) + return true; + + if (!arrayAttributes || arrayAttributes[pidx].isConfigurable()) { + arrayData[pidx].value = Value::undefinedValue(); + if (!arrayAttributes) + ensureArrayAttributes(); + arrayAttributes[pidx].clear(); + if (sparseArray) { + arrayData[pidx].value.int_32 = arrayFreeList; + arrayFreeList = pidx; + } + return true; + } + + if (engine()->current->strictMode) + engine()->current->throwTypeError(); + return false; +} + +// Section 8.12.9 +bool Object::__defineOwnProperty__(ExecutionContext *ctx, String *name, const Property &p, PropertyAttributes attrs) +{ + uint idx = name->asArrayIndex(); + if (idx != UINT_MAX) + return __defineOwnProperty__(ctx, idx, p, attrs); + + name->makeIdentifier(); + + Property *current; + PropertyAttributes *cattrs; + + if (isArrayObject() && name->isEqualTo(ctx->engine->id_length)) { + assert(ArrayObject::LengthPropertyIndex == internalClass->find(ctx->engine->id_length)); + Property *lp = memberData + ArrayObject::LengthPropertyIndex; + cattrs = internalClass->propertyData.data() + ArrayObject::LengthPropertyIndex; + if (attrs.isEmpty() || p.isSubset(attrs, *lp, *cattrs)) + return true; + if (!cattrs->isWritable() || attrs.type() == PropertyAttributes::Accessor || attrs.isConfigurable() || attrs.isEnumerable()) + goto reject; + bool succeeded = true; + if (attrs.type() == PropertyAttributes::Data) { + bool ok; + uint l = p.value.asArrayLength(&ok); + if (!ok) + ctx->throwRangeError(p.value); + succeeded = setArrayLength(l); + } + if (attrs.hasWritable() && !attrs.isWritable()) + cattrs->setWritable(false); + if (!succeeded) + goto reject; + return true; + } + + // Clause 1 + { + uint member = internalClass->find(name); + current = (member < UINT_MAX) ? memberData + member : 0; + cattrs = internalClass->propertyData.data() + member; + } + + if (!current) { + // clause 3 + if (!extensible) + goto reject; + // clause 4 + Property *pd = insertMember(name, attrs); + *pd = p; + pd->fullyPopulated(&attrs); + return true; + } + + return __defineOwnProperty__(ctx, current, name, p, attrs); +reject: + if (ctx->strictMode) + ctx->throwTypeError(); + return false; +} + +bool Object::__defineOwnProperty__(ExecutionContext *ctx, uint index, const Property &p, PropertyAttributes attrs) +{ + Property *current = 0; + + // 15.4.5.1, 4b + if (isArrayObject() && index >= arrayLength() && !internalClass->propertyData[ArrayObject::LengthPropertyIndex].isWritable()) + goto reject; + + if (isNonStrictArgumentsObject) + return static_cast(this)->defineOwnProperty(ctx, index, p, attrs); + + // Clause 1 + { + uint pidx = propertyIndexFromArrayIndex(index); + if (pidx < UINT_MAX && (!arrayAttributes || !arrayAttributes[pidx].isGeneric())) + current = arrayData + pidx; + if (!current && isStringObject()) + current = static_cast(this)->getIndex(index); + } + + if (!current) { + // clause 3 + if (!extensible) + goto reject; + // clause 4 + Property *pd = arrayInsert(index, attrs); + *pd = p; + pd->fullyPopulated(&attrs); + return true; + } + + return __defineOwnProperty__(ctx, current, 0 /*member*/, p, attrs); +reject: + if (ctx->strictMode) + ctx->throwTypeError(); + return false; +} + +bool Object::__defineOwnProperty__(ExecutionContext *ctx, Property *current, String *member, const Property &p, PropertyAttributes attrs) +{ + // clause 5 + if (attrs.isEmpty()) + return true; + + PropertyAttributes cattrs = Attr_Data; + if (member) + cattrs = internalClass->propertyData[current - memberData]; + else if (arrayAttributes) + cattrs = arrayAttributes[current - arrayData]; + + // clause 6 + if (p.isSubset(attrs, *current, cattrs)) + return true; + + // clause 7 + if (!cattrs.isConfigurable()) { + if (attrs.isConfigurable()) + goto reject; + if (attrs.hasEnumerable() && attrs.isEnumerable() != cattrs.isEnumerable()) + goto reject; + } + + // clause 8 + if (attrs.isGeneric()) + goto accept; + + // clause 9 + if (cattrs.isData() != attrs.isData()) { + // 9a + if (!cattrs.isConfigurable()) + goto reject; + if (cattrs.isData()) { + // 9b + cattrs.setType(PropertyAttributes::Accessor); + cattrs.clearWritable(); + current->setGetter(0); + current->setSetter(0); + } else { + // 9c + cattrs.setType(PropertyAttributes::Data); + cattrs.setWritable(false); + current->value = Value::undefinedValue(); + } + } else if (cattrs.isData() && attrs.isData()) { // clause 10 + if (!cattrs.isConfigurable() && !cattrs.isWritable()) { + if (attrs.isWritable() || !current->value.sameValue(p.value)) + goto reject; + } + } else { // clause 10 + assert(cattrs.isAccessor() && attrs.isAccessor()); + if (!cattrs.isConfigurable()) { + if (p.getter() && !(current->getter() == p.getter() || (!current->getter() && (quintptr)p.getter() == 0x1))) + goto reject; + if (p.setter() && !(current->setter() == p.setter() || (!current->setter() && (quintptr)p.setter() == 0x1))) + goto reject; + } + } + + accept: + + current->merge(cattrs, p, attrs); + if (member) { + internalClass = internalClass->changeMember(member, cattrs); + } else { + if (cattrs != Attr_Data) + ensureArrayAttributes(); + if (arrayAttributes) + arrayAttributes[current - arrayData] = cattrs; + } + return true; + reject: + if (ctx->strictMode) + ctx->throwTypeError(); + return false; +} + + +bool Object::__defineOwnProperty__(ExecutionContext *ctx, const QString &name, const Property &p, PropertyAttributes attrs) +{ + return __defineOwnProperty__(ctx, ctx->engine->newString(name), p, attrs); +} + + +void Object::copyArrayData(Object *other) +{ + arrayReserve(other->arrayDataLen); + arrayDataLen = other->arrayDataLen; + memcpy(arrayData, other->arrayData, arrayDataLen*sizeof(Property)); + arrayOffset = 0; + if (other->sparseArray) { + sparseArray = new SparseArray(*other->sparseArray); + arrayFreeList = other->arrayFreeList; + } + if (isArrayObject()) + setArrayLengthUnchecked(other->arrayLength()); +} + + +Value Object::arrayIndexOf(Value v, uint fromIndex, uint endIndex, ExecutionContext *ctx, Object *o) +{ + bool protoHasArray = false; + Object *p = o; + while ((p = p->prototype)) + if (p->arrayDataLen) + protoHasArray = true; + + if (protoHasArray || o->arrayAttributes) { + // lets be safe and slow + for (uint i = fromIndex; i < endIndex; ++i) { + bool exists; + Value value = o->getIndexed(i, &exists); + if (exists && __qmljs_strict_equal(value, v)) + return Value::fromDouble(i); + } + } else if (sparseArray) { + for (SparseArrayNode *n = sparseArray->lowerBound(fromIndex); n != sparseArray->end() && n->key() < endIndex; n = n->nextNode()) { + Value value = o->getValue(arrayData + n->value, arrayAttributes ? arrayAttributes[n->value] : Attr_Data); + if (__qmljs_strict_equal(value, v)) + return Value::fromDouble(n->key()); + } + } else { + if ((int) endIndex > arrayDataLen) + endIndex = arrayDataLen; + Property *pd = arrayData; + Property *end = pd + endIndex; + pd += fromIndex; + while (pd < end) { + if (!arrayAttributes || !arrayAttributes[pd - arrayData].isGeneric()) { + Value value = o->getValue(pd, arrayAttributes ? arrayAttributes[pd - arrayData] : Attr_Data); + if (__qmljs_strict_equal(value, v)) + return Value::fromDouble(pd - arrayData); + } + ++pd; + } + } + return Value::fromInt32(-1); +} + +void Object::arrayConcat(const ArrayObject *other) +{ + int newLen = arrayDataLen + other->arrayLength(); + if (other->sparseArray) + initSparse(); + // ### copy attributes as well! + if (sparseArray) { + if (other->sparseArray) { + for (const SparseArrayNode *it = other->sparseArray->begin(); it != other->sparseArray->end(); it = it->nextNode()) + arraySet(arrayDataLen + it->key(), other->arrayData + it->value); + } else { + int oldSize = arrayDataLen; + arrayReserve(oldSize + other->arrayLength()); + memcpy(arrayData + oldSize, other->arrayData, other->arrayLength()*sizeof(Property)); + if (arrayAttributes) + std::fill(arrayAttributes + oldSize, arrayAttributes + oldSize + other->arrayLength(), PropertyAttributes(Attr_Data)); + for (uint i = 0; i < other->arrayLength(); ++i) { + SparseArrayNode *n = sparseArray->insert(arrayDataLen + i); + n->value = oldSize + i; + } + } + } else { + int oldSize = arrayLength(); + arrayReserve(oldSize + other->arrayDataLen); + if (oldSize > arrayDataLen) { + ensureArrayAttributes(); + std::fill(arrayAttributes + arrayDataLen, arrayAttributes + oldSize, PropertyAttributes()); + } + arrayDataLen = oldSize + other->arrayDataLen; + if (other->arrayAttributes) { + for (int i = 0; i < arrayDataLen; ++i) { + bool exists; + arrayData[oldSize + i].value = const_cast(other)->getIndexed(i, &exists); + if (arrayAttributes) + arrayAttributes[oldSize + i] = Attr_Data; + if (!exists) { + ensureArrayAttributes(); + arrayAttributes[oldSize + i].clear(); + } + } + } else { + memcpy(arrayData + oldSize, other->arrayData, other->arrayDataLen*sizeof(Property)); + if (arrayAttributes) + std::fill(arrayAttributes + oldSize, arrayAttributes + oldSize + other->arrayDataLen, PropertyAttributes(Attr_Data)); + } + } + setArrayLengthUnchecked(newLen); +} + +void Object::arraySort(ExecutionContext *context, Object *thisObject, const Value &comparefn, uint len) +{ + if (!arrayDataLen) + return; + + if (sparseArray) { + context->throwUnimplemented("Object::sort unimplemented for sparse arrays"); + return; + } + + if (len > arrayDataLen) + len = arrayDataLen; + + // The spec says the sorting goes through a series of get,put and delete operations. + // this implies that the attributes don't get sorted around. + // behavior of accessor properties is implementation defined. We simply turn them all + // into data properties and then sort. This is in line with the sentence above. + if (arrayAttributes) { + for (uint i = 0; i < len; i++) { + if (arrayAttributes[i].isGeneric()) { + while (--len > i) + if (!arrayAttributes[len].isGeneric()) + break; + arrayData[i].value = getValue(arrayData + len, arrayAttributes[len]); + arrayAttributes[i] = Attr_Data; + arrayAttributes[len].clear(); + } else if (arrayAttributes[i].isAccessor()) { + arrayData[i].value = getValue(arrayData + i, arrayAttributes[i]); + arrayAttributes[i] = Attr_Data; + } + } + } + + ArrayElementLessThan lessThan(context, thisObject, comparefn); + + Property *begin = arrayData; + // We deliberately choose qSort over std::sort here, because with + // MSVC in debug builds, std::sort has an ASSERT() that verifies + // that the return values of lessThan are perfectly consistent + // and aborts otherwise. We do not want JavaScript to easily crash + // the entire application and therefore choose qSort, which doesn't + // have this property. + qSort(begin, begin + len, lessThan); +} + + +void Object::initSparse() +{ + if (!sparseArray) { + sparseArray = new SparseArray; + for (int i = 0; i < arrayDataLen; ++i) { + if (!arrayAttributes || !arrayAttributes[i].isGeneric()) { + SparseArrayNode *n = sparseArray->insert(i); + n->value = i + arrayOffset; + } + } + + uint off = arrayOffset; + if (!arrayOffset) { + arrayFreeList = arrayDataLen; + } else { + arrayFreeList = 0; + arrayData -= off; + arrayAlloc += off; + int o = off; + for (int i = 0; i < o - 1; ++i) { + arrayData[i].value = Value::fromInt32(i + 1); + } + arrayData[o - 1].value = Value::fromInt32(arrayDataLen + off); + } + for (int i = arrayDataLen + off; i < arrayAlloc; ++i) { + arrayData[i].value = Value::fromInt32(i + 1); + } + } +} + +void Object::arrayReserve(uint n) +{ + if (n < 8) + n = 8; + if (n >= arrayAlloc) { + uint off; + if (sparseArray) { + assert(arrayFreeList == arrayAlloc); + // ### FIXME + arrayDataLen = arrayAlloc; + off = 0; + } else { + off = arrayOffset; + } + arrayAlloc = qMax(n, 2*arrayAlloc); + Property *newArrayData = new Property[arrayAlloc]; + if (arrayData) { + memcpy(newArrayData, arrayData, sizeof(Property)*arrayDataLen); + delete [] (arrayData - off); + } + arrayData = newArrayData; + if (sparseArray) { + for (uint i = arrayFreeList; i < arrayAlloc; ++i) { + arrayData[i].value = Value::emptyValue(); + arrayData[i].value = Value::fromInt32(i + 1); + } + } else { + arrayOffset = 0; + } + + if (arrayAttributes) { + PropertyAttributes *newAttrs = new PropertyAttributes[arrayAlloc]; + memcpy(newAttrs, arrayAttributes, sizeof(PropertyAttributes)*arrayDataLen); + delete [] (arrayAttributes - off); + + arrayAttributes = newAttrs; + if (sparseArray) { + for (uint i = arrayFreeList; i < arrayAlloc; ++i) + arrayAttributes[i] = Attr_Invalid; + } + } + } +} + +void Object::ensureArrayAttributes() +{ + if (arrayAttributes) + return; + + arrayAttributes = new PropertyAttributes[arrayAlloc]; + for (uint i = 0; i < arrayDataLen; ++i) + arrayAttributes[i] = Attr_Data; + for (uint i = arrayDataLen; i < arrayAlloc; ++i) + arrayAttributes[i] = Attr_Invalid; +} + + +bool Object::setArrayLength(uint newLen) { + assert(isArrayObject()); + const Property *lengthProperty = memberData + ArrayObject::LengthPropertyIndex; + if (lengthProperty && !internalClass->propertyData[ArrayObject::LengthPropertyIndex].isWritable()) + return false; + uint oldLen = arrayLength(); + bool ok = true; + if (newLen < oldLen) { + if (sparseArray) { + SparseArrayNode *begin = sparseArray->lowerBound(newLen); + if (begin != sparseArray->end()) { + SparseArrayNode *it = sparseArray->end()->previousNode(); + while (1) { + Property &pd = arrayData[it->value]; + if (arrayAttributes) { + if (!arrayAttributes[it->value].isConfigurable()) { + ok = false; + newLen = it->key() + 1; + break; + } else { + arrayAttributes[it->value].clear(); + } + } + pd.value.tag = Value::_Empty_Type; + pd.value.int_32 = arrayFreeList; + arrayFreeList = it->value; + bool brk = (it == begin); + SparseArrayNode *prev = it->previousNode(); + sparseArray->erase(it); + if (brk) + break; + it = prev; + } + } + } else { + Property *it = arrayData + arrayDataLen; + const Property *begin = arrayData + newLen; + while (--it >= begin) { + if (arrayAttributes) { + if (!arrayAttributes[it - arrayData].isEmpty() && !arrayAttributes[it - arrayData].isConfigurable()) { + ok = false; + newLen = it - arrayData + 1; + break; + } else { + arrayAttributes[it - arrayData].clear(); + } + it->value = Value::emptyValue(); + } + } + arrayDataLen = newLen; + } + } else { + if (newLen >= 0x100000) + initSparse(); + } + setArrayLengthUnchecked(newLen); + return ok; +} + +void Object::markArrayObjects() const +{ + for (uint i = 0; i < arrayDataLen; ++i) { + const Property &pd = arrayData[i]; + if (!arrayAttributes || arrayAttributes[i].isData()) { + if (Managed *m = pd.value.asManaged()) + m->mark(); + } else if (arrayAttributes[i].isAccessor()) { + if (pd.getter()) + pd.getter()->mark(); + if (pd.setter()) + pd.setter()->mark(); + } + } +} + +ArrayObject::ArrayObject(ExecutionEngine *engine, const QStringList &list) + : Object(engine) +{ + init(engine); + + // Converts a QStringList to JS. + // The result is a new Array object with length equal to the length + // of the QStringList, and the elements being the QStringList's + // elements converted to JS Strings. + int len = list.count(); + arrayReserve(len); + for (int ii = 0; ii < len; ++ii) + arrayData[ii].value = Value::fromString(engine->newString(list.at(ii))); + setArrayLengthUnchecked(len); +} + +void ArrayObject::init(ExecutionEngine *engine) +{ + type = Type_ArrayObject; + internalClass = engine->arrayClass; + + memberData = new Property[4]; + memberData[LengthPropertyIndex].value = Value::fromInt32(0); +} + +QStringList ArrayObject::toQStringList() const +{ + QStringList result; + + QV4::ExecutionEngine *engine = internalClass->engine; + + uint32_t length = arrayLength(); + for (uint32_t i = 0; i < length; ++i) + result.append(const_cast(this)->getIndexed(i).toString(engine->current)->toQString()); + return result; +} + + +DEFINE_MANAGED_VTABLE(ForEachIteratorObject); + +void ForEachIteratorObject::markObjects(Managed *that) +{ + ForEachIteratorObject *o = static_cast(that); + Object::markObjects(that); + if (o->it.object) + o->it.object->mark(); +} diff --git a/src/qml/jsruntime/qv4object_p.h b/src/qml/jsruntime/qv4object_p.h new file mode 100644 index 0000000000..a9f947b629 --- /dev/null +++ b/src/qml/jsruntime/qv4object_p.h @@ -0,0 +1,432 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QMLJS_OBJECTS_H +#define QMLJS_OBJECTS_H + +#include "qv4global_p.h" +#include "qv4runtime_p.h" +#include "qv4engine_p.h" +#include "qv4context_p.h" +#include "qv4sparsearray_p.h" +#include "qv4string_p.h" +#include "qv4managed_p.h" +#include "qv4property_p.h" +#include "qv4internalclass_p.h" +#include "qv4objectiterator_p.h" + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct Function; +struct Lookup; +struct Object; +struct ObjectIterator; +struct BooleanObject; +struct NumberObject; +struct StringObject; +struct ArrayObject; +struct DateObject; +struct FunctionObject; +struct RegExpObject; +struct ErrorObject; +struct ArgumentsObject; +struct ExecutionContext; +struct CallContext; +struct ExecutionEngine; +class MemoryManager; + +struct ObjectPrototype; +struct StringPrototype; +struct NumberPrototype; +struct BooleanPrototype; +struct ArrayPrototype; +struct FunctionPrototype; +struct DatePrototype; +struct RegExpPrototype; +struct ErrorPrototype; +struct EvalErrorPrototype; +struct RangeErrorPrototype; +struct ReferenceErrorPrototype; +struct SyntaxErrorPrototype; +struct TypeErrorPrototype; +struct URIErrorPrototype; + +typedef Value (*PropertyEnumeratorFunction)(Object *object); +typedef PropertyAttributes (*PropertyQueryFunction)(const Object *object, String *name); + +struct Q_QML_EXPORT Object: Managed { + Object *prototype; + uint memberDataAlloc; + Property *memberData; + + union { + uint arrayFreeList; + uint arrayOffset; + }; + uint arrayDataLen; + uint arrayAlloc; + PropertyAttributes *arrayAttributes; + Property *arrayData; + SparseArray *sparseArray; + + enum { + InlinePropertySize = 4 + }; + Property inlineProperties[InlinePropertySize]; + + Object(ExecutionEngine *engine); + Object(ExecutionContext *context); + Object(ExecutionEngine *engine, InternalClass *internalClass); + ~Object(); + + Property *__getOwnProperty__(String *name, PropertyAttributes *attrs = 0); + Property *__getOwnProperty__(uint index, PropertyAttributes *attrs = 0); + + Property *__getPropertyDescriptor__(String *name, PropertyAttributes *attrs = 0) const; + Property *__getPropertyDescriptor__(uint index, PropertyAttributes *attrs = 0) const; + + bool __hasProperty__(String *name) const; + bool __hasProperty__(uint index) const { + return __getPropertyDescriptor__(index); + } + + bool __defineOwnProperty__(ExecutionContext *ctx, Property *current, String *member, const Property &p, PropertyAttributes attrs); + bool __defineOwnProperty__(ExecutionContext *ctx, String *name, const Property &p, PropertyAttributes attrs); + bool __defineOwnProperty__(ExecutionContext *ctx, uint index, const Property &p, PropertyAttributes attrs); + bool __defineOwnProperty__(ExecutionContext *ctx, const QString &name, const Property &p, PropertyAttributes attrs); + + // + // helpers + // + void put(ExecutionContext *ctx, const QString &name, const Value &value); + + static Value getValue(const Value &thisObject, const Property *p, PropertyAttributes attrs); + Value getValue(const Property *p, PropertyAttributes attrs) const { + return getValue(Value::fromObject(const_cast(this)), p, attrs); + } + + void putValue(Property *pd, PropertyAttributes attrs, const Value &value); + + void inplaceBinOp(ExecutionContext *, BinOp op, String *name, const Value &rhs); + void inplaceBinOp(ExecutionContext *ctx, BinOp op, const Value &index, const Value &rhs); + void inplaceBinOp(ExecutionContext *ctx, BinOpContext op, String *name, const Value &rhs); + void inplaceBinOp(ExecutionContext *ctx, BinOpContext op, const Value &index, const Value &rhs); + + /* The spec default: Writable: true, Enumerable: false, Configurable: true */ + void defineDefaultProperty(String *name, Value value); + void defineDefaultProperty(ExecutionContext *context, const QString &name, Value value); + void defineDefaultProperty(ExecutionEngine *engine, const QString &name, Value value); + void defineDefaultProperty(ExecutionContext *context, const QString &name, Value (*code)(SimpleCallContext *), int count = 0); + void defineDefaultProperty(ExecutionEngine *engine, const QString &name, Value (*code)(SimpleCallContext *), int count = 0); + void defineAccessorProperty(ExecutionEngine *engine, const QString &name, Value (*getter)(SimpleCallContext *), Value (*setter)(SimpleCallContext *)); + void defineAccessorProperty(String *name, Value (*getter)(SimpleCallContext *), Value (*setter)(SimpleCallContext *)); + /* Fixed: Writable: false, Enumerable: false, Configurable: false */ + void defineReadonlyProperty(ExecutionEngine *engine, const QString &name, Value value); + void defineReadonlyProperty(String *name, Value value); + + Property *insertMember(String *s, PropertyAttributes attributes); + + inline ExecutionEngine *engine() const { return internalClass->engine; } + + // Array handling + + uint allocArrayValue() { + uint idx = arrayFreeList; + if (arrayAlloc <= arrayFreeList) + arrayReserve(arrayAlloc + 1); + arrayFreeList = arrayData[arrayFreeList].value.uint_32; + if (arrayAttributes) + arrayAttributes[idx].setType(PropertyAttributes::Data); + return idx; + } + + uint allocArrayValue(Value v) { + uint idx = allocArrayValue(); + Property *pd = &arrayData[idx]; + pd->value = v; + return idx; + } + void freeArrayValue(int idx) { + Property &pd = arrayData[idx]; + pd.value.tag = Value::_Empty_Type; + pd.value.int_32 = arrayFreeList; + arrayFreeList = idx; + if (arrayAttributes) + arrayAttributes[idx].clear(); + } + + void getArrayHeadRoom() { + assert(!sparseArray && !arrayOffset); + arrayOffset = qMax(arrayDataLen >> 2, (uint)16); + Property *newArray = new Property[arrayOffset + arrayAlloc]; + memcpy(newArray + arrayOffset, arrayData, arrayDataLen*sizeof(Property)); + delete [] arrayData; + arrayData = newArray + arrayOffset; + if (arrayAttributes) { + PropertyAttributes *newAttrs = new PropertyAttributes[arrayOffset + arrayAlloc]; + memcpy(newAttrs + arrayOffset, arrayAttributes, arrayDataLen*sizeof(PropertyAttributes)); + delete [] arrayAttributes; + arrayAttributes = newAttrs + arrayOffset; + } + } + +public: + void copyArrayData(Object *other); + void initSparse(); + + uint arrayLength() const; + bool setArrayLength(uint newLen); + + void setArrayLengthUnchecked(uint l); + + Property *arrayInsert(uint index, PropertyAttributes attributes = Attr_Data) { + + Property *pd; + if (!sparseArray && (index < 0x1000 || index < arrayDataLen + (arrayDataLen >> 2))) { + if (index >= arrayAlloc) + arrayReserve(index + 1); + if (index >= arrayDataLen) { + ensureArrayAttributes(); + for (uint i = arrayDataLen; i < index; ++i) + arrayAttributes[i].clear(); + arrayDataLen = index + 1; + } + pd = arrayData + index; + } else { + initSparse(); + SparseArrayNode *n = sparseArray->insert(index); + if (n->value == UINT_MAX) + n->value = allocArrayValue(); + pd = arrayData + n->value; + } + if (index >= arrayLength()) + setArrayLengthUnchecked(index + 1); + if (arrayAttributes || attributes != Attr_Data) { + if (!arrayAttributes) + ensureArrayAttributes(); + attributes.resolve(); + arrayAttributes[pd - arrayData] = attributes; + } + return pd; + } + + void arraySet(uint index, const Property *pd) { + *arrayInsert(index) = *pd; + } + + void arraySet(uint index, Value value) { + Property *pd = arrayInsert(index); + pd->value = value; + } + + uint propertyIndexFromArrayIndex(uint index) const + { + if (!sparseArray) { + if (index >= arrayDataLen) + return UINT_MAX; + return index; + } else { + SparseArrayNode *n = sparseArray->findNode(index); + if (!n) + return UINT_MAX; + return n->value; + } + } + + Property *arrayAt(uint index) const { + uint pidx = propertyIndexFromArrayIndex(index); + if (pidx == UINT_MAX) + return 0; + return arrayData + pidx; + } + + Property *nonSparseArrayAt(uint index) const { + if (sparseArray) + return 0; + if (index >= arrayDataLen) + return 0; + return arrayData + index; + } + + void markArrayObjects() const; + + void push_back(Value v) { + uint idx = arrayLength(); + if (!sparseArray) { + if (idx >= arrayAlloc) + arrayReserve(idx + 1); + arrayData[idx].value = v; + arrayDataLen = idx + 1; + } else { + uint idx = allocArrayValue(v); + sparseArray->push_back(idx, arrayLength()); + } + setArrayLengthUnchecked(idx + 1); + } + + SparseArrayNode *sparseArrayBegin() { return sparseArray ? sparseArray->begin() : 0; } + SparseArrayNode *sparseArrayEnd() { return sparseArray ? sparseArray->end() : 0; } + + void arrayConcat(const ArrayObject *other); + void arraySort(ExecutionContext *context, Object *thisObject, const Value &comparefn, uint arrayDataLen); + Value arrayIndexOf(Value v, uint fromIndex, uint arrayDataLen, ExecutionContext *ctx, Object *o); + + void arrayReserve(uint n); + void ensureArrayAttributes(); + + inline Value get(String *name, bool *hasProperty = 0) + { return vtbl->get(this, name, hasProperty); } + inline Value getIndexed(uint idx, bool *hasProperty = 0) + { return vtbl->getIndexed(this, idx, hasProperty); } + inline void put(String *name, const Value &v) + { vtbl->put(this, name, v); } + inline void putIndexed(uint idx, const Value &v) + { vtbl->putIndexed(this, idx, v); } + using Managed::get; + using Managed::getIndexed; + using Managed::put; + using Managed::putIndexed; + using Managed::query; + using Managed::queryIndexed; + using Managed::deleteProperty; + using Managed::deleteIndexedProperty; + using Managed::getLookup; + using Managed::setLookup; + using Managed::advanceIterator; +protected: + static const ManagedVTable static_vtbl; + static void destroy(Managed *that); + static void markObjects(Managed *that); + static Value get(Managed *m, String *name, bool *hasProperty); + static Value getIndexed(Managed *m, uint index, bool *hasProperty); + static void put(Managed *m, String *name, const Value &value); + static void putIndexed(Managed *m, uint index, const Value &value); + static PropertyAttributes query(const Managed *m, String *name); + static PropertyAttributes queryIndexed(const Managed *m, uint index); + static bool deleteProperty(Managed *m, String *name); + static bool deleteIndexedProperty(Managed *m, uint index); + static void getLookup(Managed *m, Lookup *l, Value *result); + static void setLookup(Managed *m, Lookup *l, const Value &v); + static Property *advanceIterator(Managed *m, ObjectIterator *it, String **name, uint *index, PropertyAttributes *attributes); + + +private: + Value internalGet(String *name, bool *hasProperty); + Value internalGetIndexed(uint index, bool *hasProperty); + void internalPut(String *name, const Value &value); + void internalPutIndexed(uint index, const Value &value); + bool internalDeleteProperty(String *name); + bool internalDeleteIndexedProperty(uint index); + + friend struct ObjectIterator; + friend struct ObjectPrototype; +}; + +struct ForEachIteratorObject: Object { + Q_MANAGED + ObjectIterator it; + ForEachIteratorObject(ExecutionContext *ctx, Object *o) + : Object(ctx->engine), it(o, ObjectIterator::EnumerableOnly|ObjectIterator::WithProtoChain) { + vtbl = &static_vtbl; + type = Type_ForeachIteratorObject; + } + + Value nextPropertyName() { return it.nextPropertyNameAsString(); } + +protected: + static void markObjects(Managed *that); +}; + +struct BooleanObject: Object { + Value value; + BooleanObject(ExecutionEngine *engine, const Value &value): Object(engine), value(value) { type = Type_BooleanObject; } +}; + +struct NumberObject: Object { + Value value; + NumberObject(ExecutionEngine *engine, const Value &value): Object(engine), value(value) { type = Type_NumberObject; } +}; + +struct ArrayObject: Object { + enum { + LengthPropertyIndex = 0 + }; + + ArrayObject(ExecutionEngine *engine) : Object(engine) { init(engine); } + ArrayObject(ExecutionEngine *engine, const QStringList &list); + void init(ExecutionEngine *engine); + + QStringList toQStringList() const; +}; + +inline uint Object::arrayLength() const +{ + if (isArrayObject()) { + // length is always the first property of an array + Value v = memberData[ArrayObject::LengthPropertyIndex].value; + if (v.isInteger()) + return v.integerValue(); + return Value::toUInt32(v.doubleValue()); + } + return 0; +} + +inline void Object::setArrayLengthUnchecked(uint l) +{ + if (isArrayObject()) { + // length is always the first property of an array + Property &lengthProperty = memberData[ArrayObject::LengthPropertyIndex]; + lengthProperty.value = Value::fromUInt32(l); + } +} + +} + +QT_END_NAMESPACE + +#endif // QMLJS_OBJECTS_H diff --git a/src/qml/jsruntime/qv4objectiterator.cpp b/src/qml/jsruntime/qv4objectiterator.cpp new file mode 100644 index 0000000000..a89bfdb797 --- /dev/null +++ b/src/qml/jsruntime/qv4objectiterator.cpp @@ -0,0 +1,129 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "qv4objectiterator_p.h" +#include "qv4object_p.h" +#include "qv4stringobject_p.h" +#include "qv4identifier_p.h" + +using namespace QV4; + +ObjectIterator::ObjectIterator(Object *o, uint flags) + : object(o) + , current(o) + , arrayNode(0) + , arrayIndex(0) + , memberIndex(0) + , flags(flags) +{ + tmpDynamicProperty.value = Value::undefinedValue(); +} + +Property *ObjectIterator::next(String **name, uint *index, PropertyAttributes *attrs) +{ + Property *p = 0; + *name = 0; + *index = UINT_MAX; + while (1) { + if (!current) + break; + + while (p = current->advanceIterator(this, name, index, attrs)) { + // check the property is not already defined earlier in the proto chain + if (current != object) { + Property *pp; + if (*name) { + pp = object->__getPropertyDescriptor__(*name); + } else { + assert (*index != UINT_MAX); + pp = object->__getPropertyDescriptor__(*index); + } + if (pp != p) + continue; + } + return p; + } + + if (flags & WithProtoChain) + current = current->prototype; + else + current = 0; + + arrayIndex = 0; + memberIndex = 0; + } + return 0; +} + +Value ObjectIterator::nextPropertyName(Value *value) +{ + PropertyAttributes attrs; + uint index; + String *name; + Property *p = next(&name, &index, &attrs); + if (!p) + return Value::nullValue(); + + if (value) + *value = object->getValue(p, attrs); + + if (name) + return Value::fromString(name); + assert(index < UINT_MAX); + return Value::fromDouble(index); +} + +Value ObjectIterator::nextPropertyNameAsString(Value *value) +{ + PropertyAttributes attrs; + uint index; + String *name; + Property *p = next(&name, &index, &attrs); + if (!p) + return Value::nullValue(); + + if (value) + *value = object->getValue(p, attrs); + + if (name) + return Value::fromString(name); + assert(index < UINT_MAX); + return Value::fromString(object->engine()->newString(QString::number(index))); +} diff --git a/src/qml/jsruntime/qv4objectiterator_p.h b/src/qml/jsruntime/qv4objectiterator_p.h new file mode 100644 index 0000000000..95439397f5 --- /dev/null +++ b/src/qml/jsruntime/qv4objectiterator_p.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4OBJECTITERATOR_H +#define QV4OBJECTITERATOR_H + +#include "qv4global_p.h" +#include "qv4property_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct SparseArrayNode; +struct Object; +struct ArrayObject; +struct PropertyAttributes; +struct ExecutionContext; +struct Property; +struct String; +struct InternalClass; + +struct Q_QML_EXPORT ObjectIterator +{ + enum Flags { + NoFlags = 0, + EnumerableOnly = 0x1, + WithProtoChain = 0x2, + }; + + Object *object; + Object *current; + SparseArrayNode *arrayNode; + uint arrayIndex; + uint memberIndex; + uint flags; + + Property tmpDynamicProperty; + + ObjectIterator(Object *o, uint flags); + Property *next(String **name, uint *index, PropertyAttributes *attributes = 0); + Value nextPropertyName(Value *value = 0); + Value nextPropertyNameAsString(Value *value = 0); +}; + +} + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/jsruntime/qv4objectproto.cpp b/src/qml/jsruntime/qv4objectproto.cpp new file mode 100644 index 0000000000..462c9ca81e --- /dev/null +++ b/src/qml/jsruntime/qv4objectproto.cpp @@ -0,0 +1,624 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qv4objectproto_p.h" +#include "qv4mm_p.h" +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifndef Q_OS_WIN +# include +# ifndef Q_OS_VXWORKS +# include +# else +# include "qplatformdefs.h" +# endif +#else +# include +#endif + +using namespace QV4; + + +DEFINE_MANAGED_VTABLE(ObjectCtor); + +ObjectCtor::ObjectCtor(ExecutionContext *scope) + : FunctionObject(scope, scope->engine->newIdentifier(QStringLiteral("Object"))) +{ + vtbl = &static_vtbl; +} + +Value ObjectCtor::construct(Managed *that, Value *args, int argc) +{ + ObjectCtor *ctor = static_cast(that); + ExecutionEngine *v4 = that->engine(); + if (!argc || args[0].isUndefined() || args[0].isNull()) { + Object *obj = v4->newObject(); + Value proto = ctor->get(v4->id_prototype); + if (proto.isObject()) + obj->prototype = proto.objectValue(); + return Value::fromObject(obj); + } + return __qmljs_to_object(v4->current, args[0]); +} + +Value ObjectCtor::call(Managed *m, const Value &/*thisObject*/, Value *args, int argc) +{ + if (!argc || args[0].isUndefined() || args[0].isNull()) + return Value::fromObject(m->engine()->newObject()); + return __qmljs_to_object(m->engine()->current, args[0]); +} + +void ObjectPrototype::init(ExecutionContext *ctx, const Value &ctor) +{ + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("getPrototypeOf"), method_getPrototypeOf, 1); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("getOwnPropertyDescriptor"), method_getOwnPropertyDescriptor, 2); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("getOwnPropertyNames"), method_getOwnPropertyNames, 1); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("create"), method_create, 2); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("defineProperty"), method_defineProperty, 3); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("defineProperties"), method_defineProperties, 2); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("seal"), method_seal, 1); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("freeze"), method_freeze, 1); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("preventExtensions"), method_preventExtensions, 1); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("isSealed"), method_isSealed, 1); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("isFrozen"), method_isFrozen, 1); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("isExtensible"), method_isExtensible, 1); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("keys"), method_keys, 1); + + defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); + defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); + defineDefaultProperty(ctx, QStringLiteral("toLocaleString"), method_toLocaleString, 0); + defineDefaultProperty(ctx, QStringLiteral("valueOf"), method_valueOf, 0); + defineDefaultProperty(ctx, QStringLiteral("hasOwnProperty"), method_hasOwnProperty, 1); + defineDefaultProperty(ctx, QStringLiteral("isPrototypeOf"), method_isPrototypeOf, 1); + defineDefaultProperty(ctx, QStringLiteral("propertyIsEnumerable"), method_propertyIsEnumerable, 1); + defineDefaultProperty(ctx, QStringLiteral("__defineGetter__"), method_defineGetter, 2); + defineDefaultProperty(ctx, QStringLiteral("__defineSetter__"), method_defineSetter, 2); + + ExecutionEngine *v4 = ctx->engine; + Property *p = insertMember(v4->id___proto__, Attr_Accessor|Attr_NotEnumerable); + p->setGetter(v4->newBuiltinFunction(v4->rootContext, v4->id___proto__, method_get_proto)); + p->setSetter(v4->newBuiltinFunction(v4->rootContext, v4->id___proto__, method_set_proto)); +} + +Value ObjectPrototype::method_getPrototypeOf(SimpleCallContext *ctx) +{ + Value o = ctx->argument(0); + if (! o.isObject()) + ctx->throwTypeError(); + + Object *p = o.objectValue()->prototype; + return p ? Value::fromObject(p) : Value::nullValue(); +} + +Value ObjectPrototype::method_getOwnPropertyDescriptor(SimpleCallContext *ctx) +{ + Value O = ctx->argument(0); + if (!O.isObject()) + ctx->throwTypeError(); + + String *name = ctx->argument(1).toString(ctx); + PropertyAttributes attrs; + Property *desc = O.objectValue()->__getOwnProperty__(name, &attrs); + return fromPropertyDescriptor(ctx, desc, attrs); +} + +Value ObjectPrototype::method_getOwnPropertyNames(SimpleCallContext *context) +{ + Object *O = context->argumentCount ? context->arguments[0].asObject() : 0; + if (!O) + context->throwTypeError(); + + ArrayObject *array = getOwnPropertyNames(context->engine, context->arguments[0]); + return Value::fromObject(array); +} + +Value ObjectPrototype::method_create(SimpleCallContext *ctx) +{ + Value O = ctx->argument(0); + if (!O.isObject() && !O.isNull()) + ctx->throwTypeError(); + + Object *newObject = ctx->engine->newObject(); + newObject->prototype = O.asObject(); + + Value objValue = Value::fromObject(newObject); + if (ctx->argumentCount > 1 && !ctx->argument(1).isUndefined()) { + ctx->arguments[0] = objValue; + method_defineProperties(ctx); + } + + return objValue; +} + +Value ObjectPrototype::method_defineProperty(SimpleCallContext *ctx) +{ + Value O = ctx->argument(0); + if (!O.isObject()) + ctx->throwTypeError(); + + String *name = ctx->argument(1).toString(ctx); + + Value attributes = ctx->argument(2); + Property pd; + PropertyAttributes attrs; + toPropertyDescriptor(ctx, attributes, &pd, &attrs); + + if (!O.objectValue()->__defineOwnProperty__(ctx, name, pd, attrs)) + ctx->throwTypeError(); + + return O; +} + +Value ObjectPrototype::method_defineProperties(SimpleCallContext *ctx) +{ + Value O = ctx->argument(0); + if (!O.isObject()) + ctx->throwTypeError(); + + Object *o = ctx->argument(1).toObject(ctx); + + ObjectIterator it(o, ObjectIterator::EnumerableOnly); + while (1) { + uint index; + String *name; + PropertyAttributes attrs; + Property *pd = it.next(&name, &index, &attrs); + if (!pd) + break; + Property n; + PropertyAttributes nattrs; + toPropertyDescriptor(ctx, o->getValue(pd, attrs), &n, &nattrs); + bool ok; + if (name) + ok = O.objectValue()->__defineOwnProperty__(ctx, name, n, nattrs); + else + ok = O.objectValue()->__defineOwnProperty__(ctx, index, n, nattrs); + if (!ok) + ctx->throwTypeError(); + } + + return O; +} + +Value ObjectPrototype::method_seal(SimpleCallContext *ctx) +{ + if (!ctx->argument(0).isObject()) + ctx->throwTypeError(); + + Object *o = ctx->argument(0).objectValue(); + o->extensible = false; + + o->internalClass = o->internalClass->sealed(); + + o->ensureArrayAttributes(); + for (uint i = 0; i < o->arrayDataLen; ++i) { + if (!o->arrayAttributes[i].isGeneric()) + o->arrayAttributes[i].setConfigurable(false); + } + + return ctx->argument(0); +} + +Value ObjectPrototype::method_freeze(SimpleCallContext *ctx) +{ + if (!ctx->argument(0).isObject()) + ctx->throwTypeError(); + + Object *o = ctx->argument(0).objectValue(); + o->extensible = false; + + o->internalClass = o->internalClass->frozen(); + + o->ensureArrayAttributes(); + for (uint i = 0; i < o->arrayDataLen; ++i) { + if (!o->arrayAttributes[i].isGeneric()) + o->arrayAttributes[i].setConfigurable(false); + if (o->arrayAttributes[i].isData()) + o->arrayAttributes[i].setWritable(false); + } + return ctx->argument(0); +} + +Value ObjectPrototype::method_preventExtensions(SimpleCallContext *ctx) +{ + if (!ctx->argument(0).isObject()) + ctx->throwTypeError(); + + Object *o = ctx->argument(0).objectValue(); + o->extensible = false; + return ctx->argument(0); +} + +Value ObjectPrototype::method_isSealed(SimpleCallContext *ctx) +{ + if (!ctx->argument(0).isObject()) + ctx->throwTypeError(); + + Object *o = ctx->argument(0).objectValue(); + if (o->extensible) + return Value::fromBoolean(false); + + if (o->internalClass != o->internalClass->sealed()) + return Value::fromBoolean(false); + + if (!o->arrayDataLen) + return Value::fromBoolean(true); + + if (!o->arrayAttributes) + return Value::fromBoolean(false); + + for (uint i = 0; i < o->arrayDataLen; ++i) { + if (!o->arrayAttributes[i].isGeneric()) + if (o->arrayAttributes[i].isConfigurable()) + return Value::fromBoolean(false); + } + + return Value::fromBoolean(true); +} + +Value ObjectPrototype::method_isFrozen(SimpleCallContext *ctx) +{ + if (!ctx->argument(0).isObject()) + ctx->throwTypeError(); + + Object *o = ctx->argument(0).objectValue(); + if (o->extensible) + return Value::fromBoolean(false); + + if (o->internalClass != o->internalClass->frozen()) + return Value::fromBoolean(false); + + if (!o->arrayDataLen) + return Value::fromBoolean(true); + + if (!o->arrayAttributes) + return Value::fromBoolean(false); + + for (uint i = 0; i < o->arrayDataLen; ++i) { + if (!o->arrayAttributes[i].isGeneric()) + if (o->arrayAttributes[i].isConfigurable() || o->arrayAttributes[i].isWritable()) + return Value::fromBoolean(false); + } + + return Value::fromBoolean(true); +} + +Value ObjectPrototype::method_isExtensible(SimpleCallContext *ctx) +{ + if (!ctx->argument(0).isObject()) + ctx->throwTypeError(); + + Object *o = ctx->argument(0).objectValue(); + return Value::fromBoolean(o->extensible); +} + +Value ObjectPrototype::method_keys(SimpleCallContext *ctx) +{ + if (!ctx->argument(0).isObject()) + ctx->throwTypeError(); + + Object *o = ctx->argument(0).objectValue(); + + ArrayObject *a = ctx->engine->newArrayObject(); + + ObjectIterator it(o, ObjectIterator::EnumerableOnly); + while (1) { + Value name = it.nextPropertyNameAsString(); + if (name.isNull()) + break; + a->push_back(name); + } + + return Value::fromObject(a); +} + +Value ObjectPrototype::method_toString(SimpleCallContext *ctx) +{ + if (ctx->thisObject.isUndefined()) { + return Value::fromString(ctx, QStringLiteral("[object Undefined]")); + } else if (ctx->thisObject.isNull()) { + return Value::fromString(ctx, QStringLiteral("[object Null]")); + } else { + Value obj = __qmljs_to_object(ctx, ctx->thisObject); + QString className = obj.objectValue()->className(); + return Value::fromString(ctx, QString::fromUtf8("[object %1]").arg(className)); + } +} + +Value ObjectPrototype::method_toLocaleString(SimpleCallContext *ctx) +{ + Object *o = ctx->thisObject.toObject(ctx); + Value ts = o->get(ctx->engine->newString(QStringLiteral("toString"))); + FunctionObject *f = ts.asFunctionObject(); + if (!f) + ctx->throwTypeError(); + return f->call(Value::fromObject(o), 0, 0); +} + +Value ObjectPrototype::method_valueOf(SimpleCallContext *ctx) +{ + return Value::fromObject(ctx->thisObject.toObject(ctx)); +} + +Value ObjectPrototype::method_hasOwnProperty(SimpleCallContext *ctx) +{ + String *P = ctx->argument(0).toString(ctx); + Object *O = ctx->thisObject.toObject(ctx); + bool r = O->__getOwnProperty__(P) != 0; + return Value::fromBoolean(r); +} + +Value ObjectPrototype::method_isPrototypeOf(SimpleCallContext *ctx) +{ + Value V = ctx->argument(0); + if (! V.isObject()) + return Value::fromBoolean(false); + + Object *O = ctx->thisObject.toObject(ctx); + Object *proto = V.objectValue()->prototype; + while (proto) { + if (O == proto) + return Value::fromBoolean(true); + proto = proto->prototype; + } + return Value::fromBoolean(false); +} + +Value ObjectPrototype::method_propertyIsEnumerable(SimpleCallContext *ctx) +{ + String *p = ctx->argument(0).toString(ctx); + + Object *o = ctx->thisObject.toObject(ctx); + PropertyAttributes attrs; + o->__getOwnProperty__(p, &attrs); + return Value::fromBoolean(attrs.isEnumerable()); +} + +Value ObjectPrototype::method_defineGetter(SimpleCallContext *ctx) +{ + if (ctx->argumentCount < 2) + ctx->throwTypeError(); + String *prop = ctx->argument(0).toString(ctx); + + FunctionObject *f = ctx->argument(1).asFunctionObject(); + if (!f) + ctx->throwTypeError(); + + Object *o = ctx->thisObject.asObject(); + if (!o) { + if (!ctx->thisObject.isUndefined()) + return Value::undefinedValue(); + o = ctx->engine->globalObject; + } + + Property pd = Property::fromAccessor(f, 0); + o->__defineOwnProperty__(ctx, prop, pd, Attr_Accessor); + return Value::undefinedValue(); +} + +Value ObjectPrototype::method_defineSetter(SimpleCallContext *ctx) +{ + if (ctx->argumentCount < 2) + ctx->throwTypeError(); + String *prop = ctx->argument(0).toString(ctx); + + FunctionObject *f = ctx->argument(1).asFunctionObject(); + if (!f) + ctx->throwTypeError(); + + Object *o = ctx->thisObject.asObject(); + if (!o) { + if (!ctx->thisObject.isUndefined()) + return Value::undefinedValue(); + o = ctx->engine->globalObject; + } + + Property pd = Property::fromAccessor(0, f); + o->__defineOwnProperty__(ctx, prop, pd, Attr_Accessor); + return Value::undefinedValue(); +} + +Value ObjectPrototype::method_get_proto(SimpleCallContext *ctx) +{ + Object *o = ctx->thisObject.asObject(); + if (!o) + ctx->throwTypeError(); + + return Value::fromObject(o->prototype); +} + +Value ObjectPrototype::method_set_proto(SimpleCallContext *ctx) +{ + Object *o = ctx->thisObject.asObject(); + if (!o) + ctx->throwTypeError(); + + Value proto = ctx->argument(0); + bool ok = false; + if (proto.isNull()) { + o->prototype = 0; + ok = true; + } else if (Object *p = proto.asObject()) { + if (o->prototype == p) { + ok = true; + } else if (o->extensible) { + Object *pp = p; + while (pp) { + if (pp == o) + break; + pp = pp->prototype; + } + if (!pp) { + ok = true; + o->prototype = p; + } + } + } + if (!ok) + ctx->throwTypeError(QStringLiteral("Cyclic __proto__ value")); + return Value::undefinedValue(); +} + +void ObjectPrototype::toPropertyDescriptor(ExecutionContext *ctx, Value v, Property *desc, PropertyAttributes *attrs) +{ + if (!v.isObject()) + ctx->throwTypeError(); + + Object *o = v.objectValue(); + + attrs->clear(); + desc->setGetter(0); + desc->setSetter(0); + + if (o->__hasProperty__(ctx->engine->id_enumerable)) + attrs->setEnumerable(o->get(ctx->engine->id_enumerable).toBoolean()); + + if (o->__hasProperty__(ctx->engine->id_configurable)) + attrs->setConfigurable(o->get(ctx->engine->id_configurable).toBoolean()); + + if (o->__hasProperty__(ctx->engine->id_get)) { + Value get = o->get(ctx->engine->id_get); + FunctionObject *f = get.asFunctionObject(); + if (f) { + desc->setGetter(f); + } else if (get.isUndefined()) { + desc->setGetter((FunctionObject *)0x1); + } else { + ctx->throwTypeError(); + } + attrs->setType(PropertyAttributes::Accessor); + } + + if (o->__hasProperty__(ctx->engine->id_set)) { + Value set = o->get(ctx->engine->id_set); + FunctionObject *f = set.asFunctionObject(); + if (f) { + desc->setSetter(f); + } else if (set.isUndefined()) { + desc->setSetter((FunctionObject *)0x1); + } else { + ctx->throwTypeError(); + } + attrs->setType(PropertyAttributes::Accessor); + } + + if (o->__hasProperty__(ctx->engine->id_writable)) { + if (attrs->isAccessor()) + ctx->throwTypeError(); + attrs->setWritable(o->get(ctx->engine->id_writable).toBoolean()); + // writable forces it to be a data descriptor + desc->value = Value::undefinedValue(); + } + + if (o->__hasProperty__(ctx->engine->id_value)) { + if (attrs->isAccessor()) + ctx->throwTypeError(); + desc->value = o->get(ctx->engine->id_value); + attrs->setType(PropertyAttributes::Data); + } + + if (attrs->isGeneric()) + desc->value = Value::emptyValue(); +} + + +Value ObjectPrototype::fromPropertyDescriptor(ExecutionContext *ctx, const Property *desc, PropertyAttributes attrs) +{ + if (!desc) + return Value::undefinedValue(); + + ExecutionEngine *engine = ctx->engine; +// Let obj be the result of creating a new object as if by the expression new Object() where Object is the standard built-in constructor with that name. + Object *o = engine->newObject(); + + Property pd; + if (attrs.isData()) { + pd.value = desc->value; + o->__defineOwnProperty__(ctx, engine->newString(QStringLiteral("value")), pd, Attr_Data); + pd.value = Value::fromBoolean(attrs.isWritable()); + o->__defineOwnProperty__(ctx, engine->newString(QStringLiteral("writable")), pd, Attr_Data); + } else { + pd.value = desc->getter() ? Value::fromObject(desc->getter()) : Value::undefinedValue(); + o->__defineOwnProperty__(ctx, engine->newString(QStringLiteral("get")), pd, Attr_Data); + pd.value = desc->setter() ? Value::fromObject(desc->setter()) : Value::undefinedValue(); + o->__defineOwnProperty__(ctx, engine->newString(QStringLiteral("set")), pd, Attr_Data); + } + pd.value = Value::fromBoolean(attrs.isEnumerable()); + o->__defineOwnProperty__(ctx, engine->newString(QStringLiteral("enumerable")), pd, Attr_Data); + pd.value = Value::fromBoolean(attrs.isConfigurable()); + o->__defineOwnProperty__(ctx, engine->newString(QStringLiteral("configurable")), pd, Attr_Data); + + return Value::fromObject(o); +} + + +ArrayObject *ObjectPrototype::getOwnPropertyNames(ExecutionEngine *v4, const Value &o) +{ + ArrayObject *array = v4->newArrayObject(); + Object *O = o.asObject(); + if (!O) + return array; + + ObjectIterator it(O, ObjectIterator::NoFlags); + while (1) { + Value name = it.nextPropertyNameAsString(); + if (name.isNull()) + break; + array->push_back(name); + } + return array; +} diff --git a/src/qml/jsruntime/qv4objectproto_p.h b/src/qml/jsruntime/qv4objectproto_p.h new file mode 100644 index 0000000000..ca2e77ca42 --- /dev/null +++ b/src/qml/jsruntime/qv4objectproto_p.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4ECMAOBJECTS_P_H +#define QV4ECMAOBJECTS_P_H + +#include "qv4object_p.h" +#include "qv4functionobject_p.h" +#include + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct ObjectCtor: FunctionObject +{ + ObjectCtor(ExecutionContext *scope); + + static Value construct(Managed *that, Value *args, int argc); + static Value call(Managed *that, const Value &, Value *, int); + +protected: + static const ManagedVTable static_vtbl; +}; + +struct ObjectPrototype: Object +{ + ObjectPrototype(ExecutionEngine *engine) : Object(engine) {} + + void init(ExecutionContext *ctx, const Value &ctor); + + static Value method_getPrototypeOf(SimpleCallContext *ctx); + static Value method_getOwnPropertyDescriptor(SimpleCallContext *ctx); + static Value method_getOwnPropertyNames(SimpleCallContext *context); + static Value method_create(SimpleCallContext *ctx); + static Value method_defineProperty(SimpleCallContext *ctx); + static Value method_defineProperties(SimpleCallContext *ctx); + static Value method_seal(SimpleCallContext *ctx); + static Value method_freeze(SimpleCallContext *ctx); + static Value method_preventExtensions(SimpleCallContext *ctx); + static Value method_isSealed(SimpleCallContext *ctx); + static Value method_isFrozen(SimpleCallContext *ctx); + static Value method_isExtensible(SimpleCallContext *ctx); + static Value method_keys(SimpleCallContext *ctx); + + static Value method_toString(SimpleCallContext *ctx); + static Value method_toLocaleString(SimpleCallContext *ctx); + static Value method_valueOf(SimpleCallContext *ctx); + static Value method_hasOwnProperty(SimpleCallContext *ctx); + static Value method_isPrototypeOf(SimpleCallContext *ctx); + static Value method_propertyIsEnumerable(SimpleCallContext *ctx); + + static Value method_defineGetter(SimpleCallContext *ctx); + static Value method_defineSetter(SimpleCallContext *ctx); + + static Value method_get_proto(SimpleCallContext *ctx); + static Value method_set_proto(SimpleCallContext *ctx); + + static void toPropertyDescriptor(ExecutionContext *ctx, Value v, Property *desc, PropertyAttributes *attrs); + static Value fromPropertyDescriptor(ExecutionContext *ctx, const Property *desc, PropertyAttributes attrs); + + static ArrayObject *getOwnPropertyNames(ExecutionEngine *v4, const Value &o); +}; + + +} + +QT_END_NAMESPACE + +#endif // QV4ECMAOBJECTS_P_H diff --git a/src/qml/jsruntime/qv4property_p.h b/src/qml/jsruntime/qv4property_p.h new file mode 100644 index 0000000000..024ad3c720 --- /dev/null +++ b/src/qml/jsruntime/qv4property_p.h @@ -0,0 +1,150 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4PROPERTYDESCRIPTOR_H +#define QV4PROPERTYDESCRIPTOR_H + +#include "qv4global_p.h" +#include "qv4value_p.h" +#include "qv4internalclass_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct FunctionObject; + +struct Property { + union { + Value value; + struct { + FunctionObject *get; + FunctionObject *set; + }; + }; + + // Section 8.10 + inline void fullyPopulated(PropertyAttributes *attrs) { + if (!attrs->hasType()) { + value = Value::undefinedValue(); + } + if (attrs->type() == PropertyAttributes::Accessor) { + attrs->clearWritable(); + if (get == (FunctionObject *)0x1) + get = 0; + if (set == (FunctionObject *)0x1) + set = 0; + } + attrs->resolve(); + } + + static inline Property fromValue(Value v) { + Property pd; + pd.value = v; + return pd; + } + static inline Property fromAccessor(FunctionObject *getter, FunctionObject *setter) { + Property pd; + pd.get = getter; + pd.set = setter; + return pd; + } + + static Property genericDescriptor() { + Property pd; + pd.value = Value::emptyValue(); + return pd; + } + + inline bool isSubset(const PropertyAttributes &attrs, const Property &other, PropertyAttributes otherAttrs) const; + inline void merge(PropertyAttributes &attrs, const Property &other, PropertyAttributes otherAttrs); + + inline FunctionObject *getter() const { return get; } + inline FunctionObject *setter() const { return set; } + inline void setGetter(FunctionObject *g) { get = g; } + inline void setSetter(FunctionObject *s) { set = s; } +}; + +inline bool Property::isSubset(const PropertyAttributes &attrs, const Property &other, PropertyAttributes otherAttrs) const +{ + if (attrs.type() != PropertyAttributes::Generic && attrs.type() != otherAttrs.type()) + return false; + if (attrs.hasEnumerable() && attrs.isEnumerable() != otherAttrs.isEnumerable()) + return false; + if (attrs.hasConfigurable() && attrs.isConfigurable() != otherAttrs.isConfigurable()) + return false; + if (attrs.hasWritable() && attrs.isWritable() != otherAttrs.isWritable()) + return false; + if (attrs.type() == PropertyAttributes::Data && !value.sameValue(other.value)) + return false; + if (attrs.type() == PropertyAttributes::Accessor) { + if (get != other.get) + return false; + if (set != other.set) + return false; + } + return true; +} + +inline void Property::merge(PropertyAttributes &attrs, const Property &other, PropertyAttributes otherAttrs) +{ + if (otherAttrs.hasEnumerable()) + attrs.setEnumerable(otherAttrs.isEnumerable()); + if (otherAttrs.hasConfigurable()) + attrs.setConfigurable(otherAttrs.isConfigurable()); + if (otherAttrs.hasWritable()) + attrs.setWritable(otherAttrs.isWritable()); + if (otherAttrs.type() == PropertyAttributes::Accessor) { + attrs.setType(PropertyAttributes::Accessor); + if (other.get) + get = (other.get == (FunctionObject *)0x1) ? 0 : other.get; + if (other.set) + set = (other.set == (FunctionObject *)0x1) ? 0 : other.set; + } else if (otherAttrs.type() == PropertyAttributes::Data){ + attrs.setType(PropertyAttributes::Data); + value = other.value; + } +} + +} + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/jsruntime/qv4qmlextensions.cpp b/src/qml/jsruntime/qv4qmlextensions.cpp new file mode 100644 index 0000000000..a55330ff67 --- /dev/null +++ b/src/qml/jsruntime/qv4qmlextensions.cpp @@ -0,0 +1,51 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4qmlextensions_p.h" +#include "qv4object_p.h" + +using namespace QV4; + +void QmlExtensions::markObjects() +{ + if (valueTypeWrapperPrototype) + valueTypeWrapperPrototype->mark(); +} diff --git a/src/qml/jsruntime/qv4qmlextensions_p.h b/src/qml/jsruntime/qv4qmlextensions_p.h new file mode 100644 index 0000000000..cf9e287efe --- /dev/null +++ b/src/qml/jsruntime/qv4qmlextensions_p.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4QMLEXTENSIONS_P_H +#define QV4QMLEXTENSIONS_P_H + +#include + +QT_BEGIN_NAMESPACE + +namespace QV4 { +struct Object; + +struct Q_QML_EXPORT QmlExtensions +{ + QmlExtensions() + : valueTypeWrapperPrototype(0) + {} + + Object *valueTypeWrapperPrototype; + + void markObjects(); +}; + +} // namespace QV4 + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp new file mode 100644 index 0000000000..f79675845b --- /dev/null +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -0,0 +1,1792 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4qobjectwrapper_p.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +#if defined(__GNUC__) && !defined(__INTEL_COMPILER) +# if (__GNUC__ * 100 + __GNUC_MINOR__) >= 405 +// The code in this file does not violate strict aliasing, but GCC thinks it does +// so turn off the warnings for us to have a clean build +# pragma GCC diagnostic ignored "-Wstrict-aliasing" +# endif +#endif + + +using namespace QV4; + +static QPair extractQtMethod(QV4::FunctionObject *function) +{ + if (function && function->subtype == QV4::FunctionObject::WrappedQtMethod) { + QObjectMethod *method = static_cast(function); + return qMakePair(method->object(), method->methodIndex()); + } + + return qMakePair((QObject *)0, -1); +} + +static QPair extractQtSignal(const Value &value) +{ + if (QV4::FunctionObject *function = value.asFunctionObject()) + return extractQtMethod(function); + + if (QV4::QmlSignalHandler *handler = value.as()) + return qMakePair(handler->object(), handler->signalIndex()); + + return qMakePair((QObject *)0, -1); +} + + +struct ReadAccessor { + static inline void Indirect(QObject *object, const QQmlPropertyData &property, + void *output, QQmlNotifier **n) + { + Q_ASSERT(n == 0); + Q_UNUSED(n); + + void *args[] = { output, 0 }; + QMetaObject::metacall(object, QMetaObject::ReadProperty, property.coreIndex, args); + } + + static inline void Direct(QObject *object, const QQmlPropertyData &property, + void *output, QQmlNotifier **n) + { + Q_ASSERT(n == 0); + Q_UNUSED(n); + + void *args[] = { output, 0 }; + object->qt_metacall(QMetaObject::ReadProperty, property.coreIndex, args); + } + + static inline void Accessor(QObject *object, const QQmlPropertyData &property, + void *output, QQmlNotifier **n) + { + Q_ASSERT(property.accessors); + + property.accessors->read(object, property.accessorData, output); + if (n) property.accessors->notifier(object, property.accessorData, n); + } +}; + +static inline QV4::Value valueToHandle(QV4::ExecutionEngine *, int v) +{ return QV4::Value::fromInt32(v); } +static inline QV4::Value valueToHandle(QV4::ExecutionEngine *, uint v) +{ return QV4::Value::fromUInt32(v); } +static inline QV4::Value valueToHandle(QV4::ExecutionEngine *, bool v) +{ return QV4::Value::fromBoolean(v); } +static inline QV4::Value valueToHandle(QV4::ExecutionEngine *e, const QString &v) +{ return QV4::Value::fromString(e, v); } +static inline QV4::Value valueToHandle(QV4::ExecutionEngine *, float v) +{ return QV4::Value::fromDouble(v); } +static inline QV4::Value valueToHandle(QV4::ExecutionEngine *, double v) +{ return QV4::Value::fromDouble(v); } +static inline QV4::Value valueToHandle(QV4::ExecutionEngine *e, QObject *v) +{ return QV4::QObjectWrapper::wrap(e, v); } + +// Load value properties +template +static QV4::Value LoadProperty(QV8Engine *engine, QObject *object, + const QQmlPropertyData &property, + QQmlNotifier **notifier) +{ + Q_ASSERT(!property.isFunction()); + QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine); + + if (property.isQObject()) { + QObject *rv = 0; + ReadFunction(object, property, &rv, notifier); + return QV4::QObjectWrapper::wrap(v4, rv); + } else if (property.isQList()) { + return QmlListWrapper::create(engine, object, property.coreIndex, property.propType); + } else if (property.propType == QMetaType::QReal) { + qreal v = 0; + ReadFunction(object, property, &v, notifier); + return valueToHandle(v4, v); + } else if (property.propType == QMetaType::Int || property.isEnum()) { + int v = 0; + ReadFunction(object, property, &v, notifier); + return valueToHandle(v4, v); + } else if (property.propType == QMetaType::Bool) { + bool v = false; + ReadFunction(object, property, &v, notifier); + return valueToHandle(v4, v); + } else if (property.propType == QMetaType::QString) { + QString v; + ReadFunction(object, property, &v, notifier); + return valueToHandle(v4, v); + } else if (property.propType == QMetaType::UInt) { + uint v = 0; + ReadFunction(object, property, &v, notifier); + return valueToHandle(v4, v); + } else if (property.propType == QMetaType::Float) { + float v = 0; + ReadFunction(object, property, &v, notifier); + return valueToHandle(v4, v); + } else if (property.propType == QMetaType::Double) { + double v = 0; + ReadFunction(object, property, &v, notifier); + return valueToHandle(v4, v); + } else if (property.isV4Handle()) { + QQmlV4Handle handle; + ReadFunction(object, property, &handle, notifier); + return handle.toValue(); + } else if (property.propType == qMetaTypeId()) { + QJSValue v; + ReadFunction(object, property, &v, notifier); + return QJSValuePrivate::get(v)->getValue(v4); + } else if (property.isQVariant()) { + QVariant v; + ReadFunction(object, property, &v, notifier); + + if (QQmlValueTypeFactory::isValueType(v.userType())) { + if (QQmlValueType *valueType = QQmlValueTypeFactory::valueType(v.userType())) + return QV4::QmlValueTypeWrapper::create(engine, object, property.coreIndex, valueType); // VariantReference value-type. + } + + return engine->fromVariant(v); + } else if (QQmlValueTypeFactory::isValueType(property.propType)) { + Q_ASSERT(notifier == 0); + + if (QQmlValueType *valueType = QQmlValueTypeFactory::valueType(property.propType)) + return QV4::QmlValueTypeWrapper::create(engine, object, property.coreIndex, valueType); + } else { + Q_ASSERT(notifier == 0); + + // see if it's a sequence type + bool succeeded = false; + QV4::Value retn = QV4::SequencePrototype::newSequence(v4, property.propType, object, property.coreIndex, &succeeded); + if (succeeded) + return retn; + } + + if (property.propType == QMetaType::UnknownType) { + QMetaProperty p = object->metaObject()->property(property.coreIndex); + qWarning("QMetaProperty::read: Unable to handle unregistered datatype '%s' for property " + "'%s::%s'", p.typeName(), object->metaObject()->className(), p.name()); + return QV4::Value::undefinedValue(); + } else { + QVariant v(property.propType, (void *)0); + ReadFunction(object, property, v.data(), notifier); + return engine->fromVariant(v); + } +} + +QObjectWrapper::QObjectWrapper(ExecutionEngine *engine, QObject *object) + : Object(engine) + , m_object(object) +{ + vtbl = &static_vtbl; + prototype = engine->objectPrototype; + + m_destroy = engine->newIdentifier(QStringLiteral("destroy")); + m_toString = engine->newIdentifier(QStringLiteral("toString")); +} + +void QObjectWrapper::initializeBindings(ExecutionEngine *engine) +{ + engine->functionPrototype->defineDefaultProperty(engine, QStringLiteral("connect"), method_connect); + engine->functionPrototype->defineDefaultProperty(engine, QStringLiteral("disconnect"), method_disconnect); +} + +QQmlPropertyData *QObjectWrapper::findProperty(ExecutionEngine *engine, QQmlContextData *qmlContext, String *name, RevisionMode revisionMode, QQmlPropertyData *local) const +{ + QQmlData *ddata = QQmlData::get(m_object, false); + if (!ddata) + return 0; + QQmlPropertyData *result = 0; + if (ddata && ddata->propertyCache) + result = ddata->propertyCache->property(name, m_object, qmlContext); + if (!result) + result = QQmlPropertyCache::property(engine->v8Engine->engine(), m_object, name, qmlContext, *local); + return result; +} + +Value QObjectWrapper::getQmlProperty(ExecutionContext *ctx, QQmlContextData *qmlContext, String *name, QObjectWrapper::RevisionMode revisionMode, bool *hasProperty, bool includeImports) +{ + if (QQmlData::wasDeleted(m_object)) { + if (hasProperty) + *hasProperty = false; + return QV4::Value::undefinedValue(); + } + + if (name->isEqualTo(m_destroy) || name->isEqualTo(m_toString)) { + int index = name->isEqualTo(m_destroy) ? QV4::QObjectMethod::DestroyMethod : QV4::QObjectMethod::ToStringMethod; + QV4::Value method = QV4::QObjectMethod::create(ctx->engine->rootContext, m_object, index); + if (hasProperty) + *hasProperty = true; + return method; + } + + QQmlPropertyData local; + QQmlPropertyData *result = findProperty(ctx->engine, qmlContext, name, revisionMode, &local); + + if (!result) { + if (includeImports && name->startsWithUpper()) { + // Check for attached properties + if (qmlContext && qmlContext->imports) { + QQmlTypeNameCache::Result r = qmlContext->imports->query(name); + + if (hasProperty) + *hasProperty = true; + + if (r.isValid()) { + if (r.scriptIndex != -1) { + return QV4::Value::undefinedValue(); + } else if (r.type) { + return QmlTypeWrapper::create(ctx->engine->v8Engine, m_object, r.type, QmlTypeWrapper::ExcludeEnums); + } else if (r.importNamespace) { + return QmlTypeWrapper::create(ctx->engine->v8Engine, m_object, qmlContext->imports, r.importNamespace, QmlTypeWrapper::ExcludeEnums); + } + Q_ASSERT(!"Unreachable"); + } + } + } + return QV4::Object::get(this, name, hasProperty); + } + + QQmlData::flushPendingBinding(m_object, result->coreIndex); + QQmlData *ddata = QQmlData::get(m_object, false); + + if (revisionMode == QV4::QObjectWrapper::CheckRevision && result->hasRevision()) { + if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result)) { + if (hasProperty) + *hasProperty = false; + return QV4::Value::undefinedValue(); + } + } + + if (hasProperty) + *hasProperty = true; + + if (result->isFunction() && !result->isVarProperty()) { + if (result->isVMEFunction()) { + QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(m_object); + Q_ASSERT(vmemo); + return vmemo->vmeMethod(result->coreIndex); + } else if (result->isV4Function()) { + return QV4::QObjectMethod::create(ctx->engine->rootContext, m_object, result->coreIndex, QV4::Value::fromObject(ctx->engine->qmlContextObject())); + } else if (result->isSignalHandler()) { + QV4::QmlSignalHandler *handler = new (ctx->engine->memoryManager) QV4::QmlSignalHandler(ctx->engine, m_object, result->coreIndex); + + QV4::String *connect = ctx->engine->newIdentifier(QStringLiteral("connect")); + QV4::String *disconnect = ctx->engine->newIdentifier(QStringLiteral("disconnect")); + handler->put(connect, ctx->engine->functionPrototype->get(connect)); + handler->put(disconnect, ctx->engine->functionPrototype->get(disconnect)); + + return QV4::Value::fromObject(handler); + } else { + return QV4::QObjectMethod::create(ctx->engine->rootContext, m_object, result->coreIndex); + } + } + + QQmlEnginePrivate *ep = ctx->engine->v8Engine->engine() ? QQmlEnginePrivate::get(ctx->engine->v8Engine->engine()) : 0; + + if (result->hasAccessors()) { + QQmlNotifier *n = 0; + QQmlNotifier **nptr = 0; + + if (ep && ep->propertyCapture && result->accessors->notifier) + nptr = &n; + + QV4::Value rv = LoadProperty(ctx->engine->v8Engine, m_object, *result, nptr); + + if (result->accessors->notifier) { + if (n) ep->captureProperty(n); + } else { + ep->captureProperty(m_object, result->coreIndex, result->notifyIndex); + } + + return rv; + } + + if (ep && !result->isConstant()) + ep->captureProperty(m_object, result->coreIndex, result->notifyIndex); + + if (result->isVarProperty()) { + QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(m_object); + Q_ASSERT(vmemo); + return vmemo->vmeProperty(result->coreIndex); + } else if (result->isDirect()) { + return LoadProperty(ctx->engine->v8Engine, m_object, *result, 0); + } else { + return LoadProperty(ctx->engine->v8Engine, m_object, *result, 0); + } +} + +Value QObjectWrapper::getQmlProperty(ExecutionContext *ctx, QQmlContextData *qmlContext, QObject *object, String *name, QObjectWrapper::RevisionMode revisionMode, bool *hasProperty) +{ + if (QQmlData::wasDeleted(object)) { + if (hasProperty) + *hasProperty = false; + return QV4::Value::nullValue(); + } + + if (!QQmlData::get(object, true)) { + if (hasProperty) + *hasProperty = false; + return QV4::Value::nullValue(); + } + + QObjectWrapper *wrapper = wrap(ctx->engine, object).as(); + if (!wrapper) { + if (hasProperty) + *hasProperty = false; + return QV4::Value::nullValue(); + } + return wrapper->getQmlProperty(ctx, qmlContext, name, revisionMode, hasProperty); +} + +bool QObjectWrapper::setQmlProperty(ExecutionContext *ctx, QQmlContextData *qmlContext, QObject *object, String *name, QObjectWrapper::RevisionMode revisionMode, const Value &value) +{ + if (QQmlData::wasDeleted(object)) + return false; + + QQmlPropertyData local; + QQmlPropertyData *result = 0; + { + result = QQmlPropertyCache::property(ctx->engine->v8Engine->engine(), object, name, qmlContext, local); + } + + if (!result) + return false; + + if (revisionMode == QV4::QObjectWrapper::CheckRevision && result->hasRevision()) { + QQmlData *ddata = QQmlData::get(object); + if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result)) + return false; + } + + if (!result->isWritable() && !result->isQList()) { + QString error = QLatin1String("Cannot assign to read-only property \"") + + name->toQString() + QLatin1Char('\"'); + ctx->throwTypeError(error); + } + + QQmlBinding *newBinding = 0; + if (FunctionObject *f = value.asFunctionObject()) { + if (!f->bindingKeyFlag) { + if (!result->isVarProperty() && result->propType != qMetaTypeId()) { + // assigning a JS function to a non var or QJSValue property or is not allowed. + QString error = QLatin1String("Cannot assign JavaScript function to "); + if (!QMetaType::typeName(result->propType)) + error += QLatin1String("[unknown property type]"); + else + error += QLatin1String(QMetaType::typeName(result->propType)); + ctx->throwError(error); + } + } else { + // binding assignment. + QQmlContextData *callingQmlContext = QV4::QmlContextWrapper::callingContext(ctx->engine); + + QV4::ExecutionEngine::StackFrame frame = ctx->engine->currentStackFrame(); + + newBinding = new QQmlBinding(value, object, callingQmlContext, frame.source, + qmlSourceCoordinate(frame.line), qmlSourceCoordinate(frame.column)); + newBinding->setTarget(object, *result, callingQmlContext); + newBinding->setEvaluateFlags(newBinding->evaluateFlags() | + QQmlBinding::RequiresThisObject); + } + } + + QQmlAbstractBinding *oldBinding = + QQmlPropertyPrivate::setBinding(object, result->coreIndex, -1, newBinding); + if (oldBinding) + oldBinding->destroy(); + + if (!newBinding && result->isVarProperty()) { + // allow assignment of "special" values (null, undefined, function) to var properties + QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object); + Q_ASSERT(vmemo); + vmemo->setVMEProperty(result->coreIndex, value); + return true; + } + +#define PROPERTY_STORE(cpptype, value) \ + cpptype o = value; \ + int status = -1; \ + int flags = 0; \ + void *argv[] = { &o, 0, &status, &flags }; \ + QMetaObject::metacall(object, QMetaObject::WriteProperty, result->coreIndex, argv); + + if (value.isNull() && result->isQObject()) { + PROPERTY_STORE(QObject*, 0); + } else if (value.isUndefined() && result->isResettable()) { + void *a[] = { 0 }; + QMetaObject::metacall(object, QMetaObject::ResetProperty, result->coreIndex, a); + } else if (value.isUndefined() && result->propType == qMetaTypeId()) { + PROPERTY_STORE(QVariant, QVariant()); + } else if (value.isUndefined() && result->propType == QMetaType::QJsonValue) { + PROPERTY_STORE(QJsonValue, QJsonValue(QJsonValue::Undefined)); + } else if (!newBinding && result->propType == qMetaTypeId()) { + PROPERTY_STORE(QJSValue, new QJSValuePrivate(ctx->engine, value)); + } else if (value.isUndefined()) { + QString error = QLatin1String("Cannot assign [undefined] to "); + if (!QMetaType::typeName(result->propType)) + error += QLatin1String("[unknown property type]"); + else + error += QLatin1String(QMetaType::typeName(result->propType)); + ctx->throwError(error); + } else if (value.asFunctionObject()) { + // this is handled by the binding creation above + } else if (result->propType == QMetaType::Int && value.isNumber()) { + PROPERTY_STORE(int, qRound(value.asDouble())); + } else if (result->propType == QMetaType::QReal && value.isNumber()) { + PROPERTY_STORE(qreal, qreal(value.asDouble())); + } else if (result->propType == QMetaType::Float && value.isNumber()) { + PROPERTY_STORE(float, float(value.asDouble())); + } else if (result->propType == QMetaType::Double && value.isNumber()) { + PROPERTY_STORE(double, double(value.asDouble())); + } else if (result->propType == QMetaType::QString && value.isString()) { + PROPERTY_STORE(QString, value.toQString()); + } else if (result->isVarProperty()) { + QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object); + Q_ASSERT(vmemo); + vmemo->setVMEProperty(result->coreIndex, value); + } else { + QVariant v; + if (result->isQList()) + v = ctx->engine->v8Engine->toVariant(value, qMetaTypeId >()); + else + v = ctx->engine->v8Engine->toVariant(value, result->propType); + + QQmlContextData *callingQmlContext = QV4::QmlContextWrapper::callingContext(ctx->engine); + if (!QQmlPropertyPrivate::write(object, *result, v, callingQmlContext)) { + const char *valueType = 0; + if (v.userType() == QVariant::Invalid) valueType = "null"; + else valueType = QMetaType::typeName(v.userType()); + + const char *targetTypeName = QMetaType::typeName(result->propType); + if (!targetTypeName) + targetTypeName = "an unregistered type"; + + QString error = QLatin1String("Cannot assign ") + + QLatin1String(valueType) + + QLatin1String(" to ") + + QLatin1String(targetTypeName); + ctx->throwError(error); + } + } + + return true; +} + +Value QObjectWrapper::wrap(ExecutionEngine *engine, QObject *object) +{ + if (QQmlData::wasDeleted(object)) + return QV4::Value::nullValue(); + + QQmlData *ddata = QQmlData::get(object, true); + if (!ddata) + return QV4::Value::undefinedValue(); + + if (ddata->jsEngineId == engine->m_engineId && !ddata->jsWrapper.isEmpty()) { + // We own the JS object + return ddata->jsWrapper.value(); + } else if (ddata->jsWrapper.isEmpty() && + (ddata->jsEngineId == engine->m_engineId || // We own the QObject + ddata->jsEngineId == 0 || // No one owns the QObject + !ddata->hasTaintedV8Object)) { // Someone else has used the QObject, but it isn't tainted + + QV4::Value rv = create(engine, ddata, object); + ddata->jsWrapper = rv; + ddata->jsEngineId = engine->m_engineId; + return rv; + + } else { + // If this object is tainted, we have to check to see if it is in our + // tainted object list + Object *alternateWrapper = 0; + if (engine->m_multiplyWrappedQObjects && ddata->hasTaintedV8Object) + alternateWrapper = engine->m_multiplyWrappedQObjects->value(object); + + // If our tainted handle doesn't exist or has been collected, and there isn't + // a handle in the ddata, we can assume ownership of the ddata->v8object + if (ddata->jsWrapper.isEmpty() && !alternateWrapper) { + QV4::Value result = create(engine, ddata, object); + ddata->jsWrapper = result; + ddata->jsEngineId = engine->m_engineId; + return result; + } + + if (!alternateWrapper) { + alternateWrapper = create(engine, ddata, object).asObject(); + if (!engine->m_multiplyWrappedQObjects) + engine->m_multiplyWrappedQObjects = new MultiplyWrappedQObjectMap; + engine->m_multiplyWrappedQObjects->insert(object, alternateWrapper); + ddata->hasTaintedV8Object = true; + } + + return QV4::Value::fromObject(alternateWrapper); + } +} + +QV4::Value QObjectWrapper::create(ExecutionEngine *engine, QQmlData *ddata, QObject *object) +{ + QQmlEngine *qmlEngine = engine->v8Engine->engine(); + if (!ddata->propertyCache && qmlEngine) { + ddata->propertyCache = QQmlEnginePrivate::get(qmlEngine)->cache(object); + if (ddata->propertyCache) ddata->propertyCache->addref(); + } + + return Value::fromObject(new (engine->memoryManager) QV4::QObjectWrapper(engine, object)); +} + +QV4::Value QObjectWrapper::get(Managed *m, String *name, bool *hasProperty) +{ + QObjectWrapper *that = static_cast(m); + ExecutionEngine *v4 = m->engine(); + QQmlContextData *qmlContext = QV4::QmlContextWrapper::callingContext(v4); + return that->getQmlProperty(v4->current, qmlContext, name, IgnoreRevision, hasProperty, /*includeImports*/ true); +} + +void QObjectWrapper::put(Managed *m, String *name, const Value &value) +{ + QObjectWrapper *that = static_cast(m); + ExecutionEngine *v4 = m->engine(); + + if (QQmlData::wasDeleted(that->m_object)) + return; + + QQmlContextData *qmlContext = QV4::QmlContextWrapper::callingContext(v4); + if (!setQmlProperty(v4->current, qmlContext, that->m_object, name, QV4::QObjectWrapper::IgnoreRevision, value)) { + QString error = QLatin1String("Cannot assign to non-existent property \"") + + name->toQString() + QLatin1Char('\"'); + v4->current->throwError(error); + } +} + +PropertyAttributes QObjectWrapper::query(const Managed *m, String *name) +{ + const QObjectWrapper *that = static_cast(m); + ExecutionEngine *engine = that->engine(); + QQmlContextData *qmlContext = QV4::QmlContextWrapper::callingContext(engine); + QQmlPropertyData local; + if (that->findProperty(engine, qmlContext, name, IgnoreRevision, &local) + || name->isEqualTo(that->m_destroy) || name->isEqualTo(that->m_toString)) + return QV4::Attr_Data; + else + return QV4::Object::query(m, name); +} + +Property *QObjectWrapper::advanceIterator(Managed *m, ObjectIterator *it, String **name, uint *index, PropertyAttributes *attributes) +{ + *name = 0; + *index = UINT_MAX; + + QObjectWrapper *that = static_cast(m); + + if (!that->m_object) + return QV4::Object::advanceIterator(m, it, name, index, attributes); + + const QMetaObject *mo = that->m_object->metaObject(); + const int propertyCount = mo->propertyCount(); + if (it->arrayIndex < propertyCount) { + *name = that->engine()->newString(QString::fromUtf8(mo->property(it->arrayIndex).name())); + ++it->arrayIndex; + if (attributes) + *attributes = QV4::Attr_Data; + it->tmpDynamicProperty.value = that->get(*name); + return &it->tmpDynamicProperty; + } + const int methodCount = mo->methodCount(); + if (it->arrayIndex < propertyCount + methodCount) { + *name = that->engine()->newString(QString::fromUtf8(mo->method(it->arrayIndex - propertyCount).name())); + ++it->arrayIndex; + if (attributes) + *attributes = QV4::Attr_Data; + it->tmpDynamicProperty.value = that->get(*name); + return &it->tmpDynamicProperty; + } + return QV4::Object::advanceIterator(m, it, name, index, attributes); +} + +namespace QV4 { + +struct QObjectSlotDispatcher : public QtPrivate::QSlotObjectBase +{ + QV4::PersistentValue function; + QV4::PersistentValue thisObject; + int signalIndex; + + QObjectSlotDispatcher() + : QtPrivate::QSlotObjectBase(&impl) + , signalIndex(-1) + {} + + static void impl(int which, QSlotObjectBase *this_, QObject *r, void **metaArgs, bool *ret) + { + switch (which) { + case Destroy: { + delete static_cast(this_); + } + break; + case Call: { + QObjectSlotDispatcher *This = static_cast(this_); + QVarLengthArray dummy; + int *argsTypes = QQmlPropertyCache::methodParameterTypes(r, This->signalIndex, dummy, 0); + + int argCount = argsTypes ? argsTypes[0]:0; + + QV4::FunctionObject *f = This->function.value().asFunctionObject(); + QV4::ExecutionEngine *v4 = f->internalClass->engine; + QV4::ExecutionContext *ctx = v4->current; + + QVarLengthArray args(argCount); + for (int ii = 0; ii < argCount; ++ii) { + int type = argsTypes[ii + 1]; + if (type == qMetaTypeId()) { + args[ii] = v4->v8Engine->fromVariant(*((QVariant *)metaArgs[ii + 1])); + } else { + args[ii] = v4->v8Engine->fromVariant(QVariant(type, metaArgs[ii + 1])); + } + } + + try { + f->call(This->thisObject.isEmpty() ? Value::fromObject(v4->globalObject) : This->thisObject.value(), args.data(), argCount); + } catch (QV4::Exception &e) { + e.accept(ctx); + QQmlError error; + QQmlExpressionPrivate::exceptionToError(e, error); + if (error.description().isEmpty()) + error.setDescription(QString(QLatin1String("Unknown exception occurred during evaluation of connected function: %1")).arg(f->name->toQString())); + QQmlEnginePrivate::get(v4->v8Engine->engine())->warning(error); + } + } + break; + case Compare: { + QObjectSlotDispatcher *connection = static_cast(this_); + if (connection->function.isEmpty()) { + *ret = false; + return; + } + + // This is tricky. Normally the metaArgs[0] pointer is a pointer to the _function_ + // for the new-style QObject::connect. Here we use the engine pointer as sentinel + // to distinguish those type of QSlotObjectBase connections from our QML connections. + QV4::ExecutionEngine *v4 = reinterpret_cast(metaArgs[0]); + if (v4 != connection->function.engine()) { + *ret = false; + return; + } + + QV4::Value function = *reinterpret_cast(metaArgs[1]); + QV4::Value thisObject = *reinterpret_cast(metaArgs[2]); + QObject *receiverToDisconnect = reinterpret_cast(metaArgs[3]); + int slotIndexToDisconnect = *reinterpret_cast(metaArgs[4]); + + if (slotIndexToDisconnect != -1) { + // This is a QObject function wrapper + if (connection->thisObject.isEmpty() == thisObject.isEmpty() && + (connection->thisObject.isEmpty() || __qmljs_strict_equal(connection->thisObject, thisObject))) { + + QPair connectedFunctionData = extractQtMethod(connection->function.value().asFunctionObject()); + if (connectedFunctionData.first == receiverToDisconnect && + connectedFunctionData.second == slotIndexToDisconnect) { + *ret = true; + return; + } + } + } else { + // This is a normal JS function + if (__qmljs_strict_equal(connection->function, function) && + connection->thisObject.isEmpty() == thisObject.isEmpty() && + (connection->thisObject.isEmpty() || __qmljs_strict_equal(connection->thisObject, thisObject))) { + *ret = true; + return; + } + } + + *ret = false; + } + break; + case NumOperations: + break; + } + }; +}; + +} // namespace QV4 + +Value QObjectWrapper::method_connect(SimpleCallContext *ctx) +{ + if (ctx->argumentCount == 0) + V4THROW_ERROR("Function.prototype.connect: no arguments given"); + + QPair signalInfo = extractQtSignal(ctx->thisObject); + QObject *signalObject = signalInfo.first; + int signalIndex = signalInfo.second; + + if (signalIndex < 0) + V4THROW_ERROR("Function.prototype.connect: this object is not a signal"); + + if (!signalObject) + V4THROW_ERROR("Function.prototype.connect: cannot connect to deleted QObject"); + + if (signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal) + V4THROW_ERROR("Function.prototype.connect: this object is not a signal"); + + QV4::QObjectSlotDispatcher *slot = new QV4::QObjectSlotDispatcher; + slot->signalIndex = signalIndex; + + if (ctx->argumentCount == 1) { + slot->function = ctx->arguments[0]; + } else if (ctx->argumentCount >= 2) { + slot->thisObject = ctx->arguments[0]; + slot->function = ctx->arguments[1]; + } + + if (!slot->function.value().asFunctionObject()) + V4THROW_ERROR("Function.prototype.connect: target is not a function"); + + if (!slot->thisObject.isEmpty() && !slot->thisObject.value().isObject()) + V4THROW_ERROR("Function.prototype.connect: target this is not an object"); + + QObjectPrivate::connect(signalObject, signalIndex, slot, Qt::AutoConnection); + + return QV4::Value::undefinedValue(); +} + +Value QObjectWrapper::method_disconnect(SimpleCallContext *ctx) +{ + if (ctx->argumentCount == 0) + V4THROW_ERROR("Function.prototype.disconnect: no arguments given"); + + QPair signalInfo = extractQtSignal(ctx->thisObject); + QObject *signalObject = signalInfo.first; + int signalIndex = signalInfo.second; + + if (signalIndex == -1) + V4THROW_ERROR("Function.prototype.disconnect: this object is not a signal"); + + if (!signalObject) + V4THROW_ERROR("Function.prototype.disconnect: cannot disconnect from deleted QObject"); + + if (signalIndex < 0 || signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal) + V4THROW_ERROR("Function.prototype.disconnect: this object is not a signal"); + + QV4::Value functionValue = QV4::Value::emptyValue(); + QV4::Value functionThisValue = QV4::Value::emptyValue(); + + if (ctx->argumentCount == 1) { + functionValue = ctx->arguments[0]; + } else if (ctx->argumentCount >= 2) { + functionThisValue = ctx->arguments[0]; + functionValue = ctx->arguments[1]; + } + + if (!functionValue.asFunctionObject()) + V4THROW_ERROR("Function.prototype.disconnect: target is not a function"); + + if (!functionThisValue.isEmpty() && !functionThisValue.isObject()) + V4THROW_ERROR("Function.prototype.disconnect: target this is not an object"); + + QPair functionData = extractQtMethod(functionValue.asFunctionObject()); + + void *a[] = { + ctx->engine, + &functionValue, + &functionThisValue, + functionData.first, + &functionData.second + }; + + QObjectPrivate::disconnect(signalObject, signalIndex, reinterpret_cast(&a)); + + return QV4::Value::undefinedValue(); +} + +static void markChildQObjectsRecursively(QObject *parent) +{ + const QObjectList &children = parent->children(); + for (int i = 0; i < children.count(); ++i) { + QObject *child = children.at(i); + QQmlData *ddata = QQmlData::get(child, /*create*/false); + if (ddata) + ddata->jsWrapper.markOnce(); + markChildQObjectsRecursively(child); + } +} + +void QObjectWrapper::markObjects(Managed *that) +{ + QObjectWrapper *This = static_cast(that); + + if (QObject *o = This->m_object.data()) { + QQmlVMEMetaObject *vme = QQmlVMEMetaObject::get(o); + if (vme) + vme->mark(); + + // Children usually don't need to be marked, the gc keeps them alive. + // But in the rare case of a "floating" QObject without a parent that + // _gets_ marked (we've been called here!) then we also need to + // propagate the marking down to the children recursively. + if (!o->parent()) + markChildQObjectsRecursively(o); + } + + QV4::Object::markObjects(that); +} + +namespace { + struct QObjectDeleter : public QV4::GCDeletable + { + QObjectDeleter(QObject *o) + : m_objectToDelete(o) + {} + ~QObjectDeleter() + { + QQmlData *ddata = QQmlData::get(m_objectToDelete, false); + if (ddata && ddata->ownContext && ddata->context) + ddata->context->emitDestruction(); + // This object is notionally destroyed now + ddata->isQueuedForDeletion = true; + if (lastCall) + delete m_objectToDelete; + else + m_objectToDelete->deleteLater(); + } + + QObject *m_objectToDelete; + }; +} + +void QObjectWrapper::collectDeletables(Managed *m, GCDeletable **deletable) +{ + QObjectWrapper *This = static_cast(m); + QPointer &object = This->m_object; + if (!object) + return; + + QQmlData *ddata = QQmlData::get(object, false); + if (!ddata) + return; + + if (object->parent() || ddata->indestructible) + return; + + QObjectDeleter *deleter = new QObjectDeleter(object); + object = 0; + deleter->next = *deletable; + *deletable = deleter; +} + +DEFINE_MANAGED_VTABLE_WITH_DELETABLES(QObjectWrapper); + +// XXX TODO: Need to review all calls to QQmlEngine *engine() to confirm QObjects work +// correctly in a worker thread + +namespace { + +template +class MaxSizeOf8 { + template + struct SMax { + char dummy[sizeof(Z) > sizeof(X) ? sizeof(Z) : sizeof(X)]; + }; +public: + static const size_t Size = sizeof(SMax > > > > > >); +}; + +struct CallArgument { + inline CallArgument(); + inline ~CallArgument(); + inline void *dataPtr(); + + inline void initAsType(int type); + inline void fromValue(int type, QV8Engine *, const QV4::Value&); + inline QV4::Value toValue(QV8Engine *); + +private: + CallArgument(const CallArgument &); + + inline void cleanup(); + + union { + float floatValue; + double doubleValue; + quint32 intValue; + bool boolValue; + QObject *qobjectPtr; + + char allocData[MaxSizeOf8, + QJSValue, + QQmlV4Handle, + QJsonArray, + QJsonObject, + QJsonValue>::Size]; + qint64 q_for_alignment; + }; + + // Pointers to allocData + union { + QString *qstringPtr; + QVariant *qvariantPtr; + QList *qlistPtr; + QJSValue *qjsValuePtr; + QQmlV4Handle *handlePtr; + QJsonArray *jsonArrayPtr; + QJsonObject *jsonObjectPtr; + QJsonValue *jsonValuePtr; + }; + + int type; +}; +} + +namespace { +struct CallArgs +{ + CallArgs(int length, QV4::Value *args) : _length(length), _args(args) {} + int Length() const { return _length; } + QV4::Value operator[](int idx) { return _args[idx]; } + +private: + int _length; + QV4::Value *_args; +}; +} + +static QV4::Value CallMethod(QObject *object, int index, int returnType, int argCount, + int *argTypes, QV8Engine *engine, CallArgs &callArgs) +{ + if (argCount > 0) { + + // Special handling is required for value types. + // We need to save the current value in a temporary, + // and reapply it after converting all arguments. + // This avoids the "overwriting copy-value-type-value" + // problem during Q_INVOKABLE function invocation. + QQmlValueType *valueTypeObject = qobject_cast(object); + QVariant valueTypeValue; + if (valueTypeObject) + valueTypeValue = valueTypeObject->value(); + + // Convert all arguments. + QVarLengthArray args(argCount + 1); + args[0].initAsType(returnType); + for (int ii = 0; ii < argCount; ++ii) + args[ii + 1].fromValue(argTypes[ii], engine, callArgs[ii]); + QVarLengthArray argData(args.count()); + for (int ii = 0; ii < args.count(); ++ii) + argData[ii] = args[ii].dataPtr(); + + // Reinstate saved value type object value if required. + if (valueTypeObject) + valueTypeObject->setValue(valueTypeValue); + + QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, argData.data()); + + return args[0].toValue(engine); + + } else if (returnType != QMetaType::Void) { + + CallArgument arg; + arg.initAsType(returnType); + + void *args[] = { arg.dataPtr() }; + + QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args); + + return arg.toValue(engine); + + } else { + + void *args[] = { 0 }; + QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args); + return QV4::Value::undefinedValue(); + + } +} + +/*! + Returns the match score for converting \a actual to be of type \a conversionType. A + zero score means "perfect match" whereas a higher score is worse. + + The conversion table is copied out of the \l QScript::callQtMethod() function. +*/ +static int MatchScore(const QV4::Value &actual, int conversionType) +{ + if (actual.isNumber()) { + switch (conversionType) { + case QMetaType::Double: + return 0; + case QMetaType::Float: + return 1; + case QMetaType::LongLong: + case QMetaType::ULongLong: + return 2; + case QMetaType::Long: + case QMetaType::ULong: + return 3; + case QMetaType::Int: + case QMetaType::UInt: + return 4; + case QMetaType::Short: + case QMetaType::UShort: + return 5; + break; + case QMetaType::Char: + case QMetaType::UChar: + return 6; + case QMetaType::QJsonValue: + return 5; + default: + return 10; + } + } else if (actual.isString()) { + switch (conversionType) { + case QMetaType::QString: + return 0; + case QMetaType::QJsonValue: + return 5; + default: + return 10; + } + } else if (actual.isBoolean()) { + switch (conversionType) { + case QMetaType::Bool: + return 0; + case QMetaType::QJsonValue: + return 5; + default: + return 10; + } + } else if (actual.asDateObject()) { + switch (conversionType) { + case QMetaType::QDateTime: + return 0; + case QMetaType::QDate: + return 1; + case QMetaType::QTime: + return 2; + default: + return 10; + } + } else if (actual.as()) { + switch (conversionType) { + case QMetaType::QRegExp: + return 0; + default: + return 10; + } + } else if (actual.asArrayObject()) { + switch (conversionType) { + case QMetaType::QJsonArray: + return 3; + case QMetaType::QStringList: + case QMetaType::QVariantList: + return 5; + case QMetaType::QVector4D: + case QMetaType::QMatrix4x4: + return 6; + case QMetaType::QVector3D: + return 7; + default: + return 10; + } + } else if (actual.isNull()) { + switch (conversionType) { + case QMetaType::VoidStar: + case QMetaType::QObjectStar: + case QMetaType::QJsonValue: + return 0; + default: { + const char *typeName = QMetaType::typeName(conversionType); + if (typeName && typeName[strlen(typeName) - 1] == '*') + return 0; + else + return 10; + } + } + } else if (QV4::Object *obj = actual.asObject()) { + QV8Engine *engine = obj->engine()->v8Engine; + + if (QV4::VariantObject *v = obj->as()) { + if (conversionType == qMetaTypeId()) + return 0; + if (engine->toVariant(actual, -1).userType() == conversionType) + return 0; + else + return 10; + } + + if (obj->as()) { + switch (conversionType) { + case QMetaType::QObjectStar: + return 0; + default: + return 10; + } + } + + if (obj->as()) { + if (engine->toVariant(actual, -1).userType() == conversionType) + return 0; + return 10; + } else if (conversionType == QMetaType::QJsonObject) { + return 5; + } else { + return 10; + } + + } else { + return 10; + } +} + +static inline int QMetaObject_methods(const QMetaObject *metaObject) +{ + struct Private + { + int revision; + int className; + int classInfoCount, classInfoData; + int methodCount, methodData; + }; + + return reinterpret_cast(metaObject->d.data)->methodCount; +} + +/*! +Returns the next related method, if one, or 0. +*/ +static const QQmlPropertyData * RelatedMethod(QObject *object, + const QQmlPropertyData *current, + QQmlPropertyData &dummy) +{ + QQmlPropertyCache *cache = QQmlData::get(object)->propertyCache; + if (!current->isOverload()) + return 0; + + Q_ASSERT(!current->overrideIndexIsProperty); + + if (cache) { + return cache->method(current->overrideIndex); + } else { + const QMetaObject *mo = object->metaObject(); + int methodOffset = mo->methodCount() - QMetaObject_methods(mo); + + while (methodOffset > current->overrideIndex) { + mo = mo->superClass(); + methodOffset -= QMetaObject_methods(mo); + } + + QMetaMethod method = mo->method(current->overrideIndex); + dummy.load(method); + + // Look for overloaded methods + QByteArray methodName = method.name(); + for (int ii = current->overrideIndex - 1; ii >= methodOffset; --ii) { + if (methodName == mo->method(ii).name()) { + dummy.setFlags(dummy.getFlags() | QQmlPropertyData::IsOverload); + dummy.overrideIndexIsProperty = 0; + dummy.overrideIndex = ii; + return &dummy; + } + } + + return &dummy; + } +} + +static QV4::Value CallPrecise(QObject *object, const QQmlPropertyData &data, + QV8Engine *engine, CallArgs &callArgs) +{ + QByteArray unknownTypeError; + + int returnType = QQmlPropertyCache::methodReturnType(object, data, &unknownTypeError); + + if (returnType == QMetaType::UnknownType) { + QString typeName = QString::fromLatin1(unknownTypeError); + QString error = QString::fromLatin1("Unknown method return type: %1").arg(typeName); + QV8Engine::getV4(engine)->current->throwError(error); + } + + if (data.hasArguments()) { + + int *args = 0; + QVarLengthArray dummy; + + args = QQmlPropertyCache::methodParameterTypes(object, data.coreIndex, dummy, + &unknownTypeError); + + if (!args) { + QString typeName = QString::fromLatin1(unknownTypeError); + QString error = QString::fromLatin1("Unknown method parameter type: %1").arg(typeName); + QV8Engine::getV4(engine)->current->throwError(error); + } + + if (args[0] > callArgs.Length()) { + QString error = QLatin1String("Insufficient arguments"); + QV8Engine::getV4(engine)->current->throwError(error); + } + + return CallMethod(object, data.coreIndex, returnType, args[0], args + 1, engine, callArgs); + + } else { + + return CallMethod(object, data.coreIndex, returnType, 0, 0, engine, callArgs); + + } +} + +/*! +Resolve the overloaded method to call. The algorithm works conceptually like this: + 1. Resolve the set of overloads it is *possible* to call. + Impossible overloads include those that have too many parameters or have parameters + of unknown type. + 2. Filter the set of overloads to only contain those with the closest number of + parameters. + For example, if we are called with 3 parameters and there are 2 overloads that + take 2 parameters and one that takes 3, eliminate the 2 parameter overloads. + 3. Find the best remaining overload based on its match score. + If two or more overloads have the same match score, call the last one. The match + score is constructed by adding the matchScore() result for each of the parameters. +*/ +static QV4::Value CallOverloaded(QObject *object, const QQmlPropertyData &data, + QV8Engine *engine, CallArgs &callArgs) +{ + int argumentCount = callArgs.Length(); + + const QQmlPropertyData *best = 0; + int bestParameterScore = INT_MAX; + int bestMatchScore = INT_MAX; + + // Special handling is required for value types. + // We need to save the current value in a temporary, + // and reapply it after converting all arguments. + // This avoids the "overwriting copy-value-type-value" + // problem during Q_INVOKABLE function invocation. + QQmlValueType *valueTypeObject = qobject_cast(object); + QVariant valueTypeValue; + if (valueTypeObject) + valueTypeValue = valueTypeObject->value(); + + QQmlPropertyData dummy; + const QQmlPropertyData *attempt = &data; + + do { + QVarLengthArray dummy; + int methodArgumentCount = 0; + int *methodArgTypes = 0; + if (attempt->hasArguments()) { + typedef QQmlPropertyCache PC; + int *args = PC::methodParameterTypes(object, attempt->coreIndex, dummy, 0); + if (!args) // Must be an unknown argument + continue; + + methodArgumentCount = args[0]; + methodArgTypes = args + 1; + } + + if (methodArgumentCount > argumentCount) + continue; // We don't have sufficient arguments to call this method + + int methodParameterScore = argumentCount - methodArgumentCount; + if (methodParameterScore > bestParameterScore) + continue; // We already have a better option + + int methodMatchScore = 0; + for (int ii = 0; ii < methodArgumentCount; ++ii) + methodMatchScore += MatchScore(callArgs[ii], methodArgTypes[ii]); + + if (bestParameterScore > methodParameterScore || bestMatchScore > methodMatchScore) { + best = attempt; + bestParameterScore = methodParameterScore; + bestMatchScore = methodMatchScore; + } + + if (bestParameterScore == 0 && bestMatchScore == 0) + break; // We can't get better than that + + } while((attempt = RelatedMethod(object, attempt, dummy)) != 0); + + if (best) { + if (valueTypeObject) + valueTypeObject->setValue(valueTypeValue); + return CallPrecise(object, *best, engine, callArgs); + } else { + QString error = QLatin1String("Unable to determine callable overload. Candidates are:"); + const QQmlPropertyData *candidate = &data; + while (candidate) { + error += QLatin1String("\n ") + + QString::fromUtf8(object->metaObject()->method(candidate->coreIndex).methodSignature().constData()); + candidate = RelatedMethod(object, candidate, dummy); + } + + QV8Engine::getV4(engine)->current->throwError(error); + } +} + +CallArgument::CallArgument() +: type(QVariant::Invalid) +{ +} + +CallArgument::~CallArgument() +{ + cleanup(); +} + +void CallArgument::cleanup() +{ + if (type == QMetaType::QString) { + qstringPtr->~QString(); + } else if (type == -1 || type == QMetaType::QVariant) { + qvariantPtr->~QVariant(); + } else if (type == qMetaTypeId()) { + qjsValuePtr->~QJSValue(); + } else if (type == qMetaTypeId >()) { + qlistPtr->~QList(); + } else if (type == QMetaType::QJsonArray) { + jsonArrayPtr->~QJsonArray(); + } else if (type == QMetaType::QJsonObject) { + jsonObjectPtr->~QJsonObject(); + } else if (type == QMetaType::QJsonValue) { + jsonValuePtr->~QJsonValue(); + } +} + +void *CallArgument::dataPtr() +{ + if (type == -1) + return qvariantPtr->data(); + else + return (void *)&allocData; +} + +void CallArgument::initAsType(int callType) +{ + if (type != 0) { cleanup(); type = 0; } + if (callType == QMetaType::UnknownType) return; + + if (callType == qMetaTypeId()) { + qjsValuePtr = new (&allocData) QJSValue(); + type = callType; + } else if (callType == QMetaType::Int || + callType == QMetaType::UInt || + callType == QMetaType::Bool || + callType == QMetaType::Double || + callType == QMetaType::Float) { + type = callType; + } else if (callType == QMetaType::QObjectStar) { + qobjectPtr = 0; + type = callType; + } else if (callType == QMetaType::QString) { + qstringPtr = new (&allocData) QString(); + type = callType; + } else if (callType == QMetaType::QVariant) { + type = callType; + qvariantPtr = new (&allocData) QVariant(); + } else if (callType == qMetaTypeId >()) { + type = callType; + qlistPtr = new (&allocData) QList(); + } else if (callType == qMetaTypeId()) { + type = callType; + handlePtr = new (&allocData) QQmlV4Handle; + } else if (callType == QMetaType::QJsonArray) { + type = callType; + jsonArrayPtr = new (&allocData) QJsonArray(); + } else if (callType == QMetaType::QJsonObject) { + type = callType; + jsonObjectPtr = new (&allocData) QJsonObject(); + } else if (callType == QMetaType::QJsonValue) { + type = callType; + jsonValuePtr = new (&allocData) QJsonValue(); + } else if (callType == QMetaType::Void) { + type = -1; + qvariantPtr = new (&allocData) QVariant(); + } else { + type = -1; + qvariantPtr = new (&allocData) QVariant(callType, (void *)0); + } +} + +void CallArgument::fromValue(int callType, QV8Engine *engine, const QV4::Value &value) +{ + if (type != 0) { cleanup(); type = 0; } + + if (callType == qMetaTypeId()) { + qjsValuePtr = new (&allocData) QJSValue(new QJSValuePrivate(QV8Engine::getV4(engine), value)); + type = qMetaTypeId(); + } else if (callType == QMetaType::Int) { + intValue = quint32(value.toInt32()); + type = callType; + } else if (callType == QMetaType::UInt) { + intValue = quint32(value.toUInt32()); + type = callType; + } else if (callType == QMetaType::Bool) { + boolValue = value.toBoolean(); + type = callType; + } else if (callType == QMetaType::Double) { + doubleValue = double(value.toNumber()); + type = callType; + } else if (callType == QMetaType::Float) { + floatValue = float(value.toNumber()); + type = callType; + } else if (callType == QMetaType::QString) { + if (value.isNull() || value.isUndefined()) + qstringPtr = new (&allocData) QString(); + else + qstringPtr = new (&allocData) QString(value.toQString()); + type = callType; + } else if (callType == QMetaType::QObjectStar) { + qobjectPtr = 0; + if (QV4::QObjectWrapper *qobjectWrapper = value.as()) + qobjectPtr = qobjectWrapper->object(); + type = callType; + } else if (callType == qMetaTypeId()) { + qvariantPtr = new (&allocData) QVariant(engine->toVariant(value, -1)); + type = callType; + } else if (callType == qMetaTypeId >()) { + qlistPtr = new (&allocData) QList(); + if (QV4::ArrayObject *array = value.asArrayObject()) { + uint32_t length = array->arrayLength(); + for (uint32_t ii = 0; ii < length; ++ii) { + QObject *o = 0; + if (QV4::QObjectWrapper *qobjectWrapper = array->getIndexed(ii).as()) + o = qobjectWrapper->object(); + qlistPtr->append(o); + } + } else { + QObject *o = 0; + if (QV4::QObjectWrapper *qobjectWrapper = value.as()) + o = qobjectWrapper->object(); + qlistPtr->append(o); + } + type = callType; + } else if (callType == qMetaTypeId()) { + handlePtr = new (&allocData) QQmlV4Handle(QQmlV4Handle(value)); + type = callType; + } else if (callType == QMetaType::QJsonArray) { + jsonArrayPtr = new (&allocData) QJsonArray(QV4::JsonObject::toJsonArray(value.asArrayObject())); + type = callType; + } else if (callType == QMetaType::QJsonObject) { + jsonObjectPtr = new (&allocData) QJsonObject(QV4::JsonObject::toJsonObject(value.asObject())); + type = callType; + } else if (callType == QMetaType::QJsonValue) { + jsonValuePtr = new (&allocData) QJsonValue(QV4::JsonObject::toJsonValue(value)); + type = callType; + } else if (callType == QMetaType::Void) { + *qvariantPtr = QVariant(); + } else { + qvariantPtr = new (&allocData) QVariant(); + type = -1; + + QQmlEnginePrivate *ep = engine->engine() ? QQmlEnginePrivate::get(engine->engine()) : 0; + QVariant v = engine->toVariant(value, -1); // why -1 instead of callType? + + if (v.userType() == callType) { + *qvariantPtr = v; + } else if (v.canConvert(callType)) { + *qvariantPtr = v; + qvariantPtr->convert(callType); + } else if (QV4::SequencePrototype::isSequenceType(callType) && v.userType() == qMetaTypeId()) { + // convert the JS array to a sequence of the correct type. + QVariant seqV = engine->toVariant(value, callType); + *qvariantPtr = seqV; + } else { + QQmlMetaObject mo = ep ? ep->rawMetaObjectForType(callType) : QQmlMetaObject(); + if (!mo.isNull()) { + QObject *obj = ep->toQObject(v); + + if (obj != 0 && !QQmlMetaObject::canConvert(obj, mo)) + obj = 0; + + *qvariantPtr = QVariant(callType, &obj); + } else { + *qvariantPtr = QVariant(callType, (void *)0); + } + } + } +} + +QV4::Value CallArgument::toValue(QV8Engine *engine) +{ + QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine); + if (type == qMetaTypeId()) { + return QJSValuePrivate::get(*qjsValuePtr)->getValue(v4); + } else if (type == QMetaType::Int) { + return QV4::Value::fromInt32(int(intValue)); + } else if (type == QMetaType::UInt) { + return QV4::Value::fromUInt32(intValue); + } else if (type == QMetaType::Bool) { + return QV4::Value::fromBoolean(boolValue); + } else if (type == QMetaType::Double) { + return QV4::Value::fromDouble(doubleValue); + } else if (type == QMetaType::Float) { + return QV4::Value::fromDouble(floatValue); + } else if (type == QMetaType::QString) { + return engine->toString(*qstringPtr); + } else if (type == QMetaType::QObjectStar) { + QObject *object = qobjectPtr; + if (object) + QQmlData::get(object, true)->setImplicitDestructible(); + return QV4::QObjectWrapper::wrap(v4, object); + } else if (type == qMetaTypeId >()) { + // XXX Can this be made more by using Array as a prototype and implementing + // directly against QList? + QList &list = *qlistPtr; + QV4::ArrayObject *array = v4->newArrayObject(); + array->arrayReserve(list.count()); + array->arrayDataLen = list.count(); + for (int ii = 0; ii < list.count(); ++ii) + array->arrayData[ii].value = QV4::QObjectWrapper::wrap(v4, list.at(ii)); + array->setArrayLengthUnchecked(list.count()); + return QV4::Value::fromObject(array); + } else if (type == qMetaTypeId()) { + return handlePtr->toValue(); + } else if (type == QMetaType::QJsonArray) { + return QV4::JsonObject::fromJsonArray(v4, *jsonArrayPtr); + } else if (type == QMetaType::QJsonObject) { + return QV4::JsonObject::fromJsonObject(v4, *jsonObjectPtr); + } else if (type == QMetaType::QJsonValue) { + return QV4::JsonObject::fromJsonValue(v4, *jsonValuePtr); + } else if (type == -1 || type == qMetaTypeId()) { + QVariant value = *qvariantPtr; + QV4::Value rv = engine->fromVariant(value); + if (QV4::QObjectWrapper *qobjectWrapper = rv.as()) { + if (QObject *object = qobjectWrapper->object()) + QQmlData::get(object, true)->setImplicitDestructible(); + } + return rv; + } else { + return QV4::Value::undefinedValue(); + } +} + +Value QObjectMethod::create(ExecutionContext *scope, QObject *object, int index, const Value &qmlGlobal) +{ + return Value::fromObject(new (scope->engine->memoryManager) QObjectMethod(scope, object, index, qmlGlobal)); +} + +QObjectMethod::QObjectMethod(ExecutionContext *scope, QObject *object, int index, const Value &qmlGlobal) + : FunctionObject(scope) + , m_object(object) + , m_index(index) + , m_qmlGlobal(qmlGlobal) +{ + vtbl = &static_vtbl; + subtype = WrappedQtMethod; +} + +QV4::Value QObjectMethod::method_toString(QV4::ExecutionContext *ctx) +{ + QString result; + if (m_object) { + QString objectName = m_object->objectName(); + + result += QString::fromUtf8(m_object->metaObject()->className()); + result += QLatin1String("(0x"); + result += QString::number((quintptr)m_object.data(),16); + + if (!objectName.isEmpty()) { + result += QLatin1String(", \""); + result += objectName; + result += QLatin1Char('\"'); + } + + result += QLatin1Char(')'); + } else { + result = QLatin1String("null"); + } + + return QV4::Value::fromString(ctx, result); +} + +QV4::Value QObjectMethod::method_destroy(QV4::ExecutionContext *ctx, Value *args, int argc) +{ + if (!m_object) + return QV4::Value::undefinedValue(); + if (QQmlData::keepAliveDuringGarbageCollection(m_object)) + ctx->throwError(QStringLiteral("Invalid attempt to destroy() an indestructible object")); + + int delay = 0; + if (argc > 0) + delay = args[0].toUInt32(); + + if (delay > 0) + QTimer::singleShot(delay, m_object, SLOT(deleteLater())); + else + m_object->deleteLater(); + + return QV4::Value::undefinedValue(); +} + +Value QObjectMethod::call(Managed *m, const Value &thisObject, Value *args, int argc) +{ + QObjectMethod *This = static_cast(m); + return This->callInternal(thisObject, args, argc); +} + +Value QObjectMethod::callInternal(const Value &, Value *args, int argc) +{ + ExecutionContext *context = engine()->current; + if (m_index == DestroyMethod) + return method_destroy(context, args, argc); + else if (m_index == ToStringMethod) + return method_toString(context); + + QObject *object = m_object.data(); + if (!object) + return QV4::Value::undefinedValue(); + + QQmlData *ddata = QQmlData::get(object); + if (!ddata) + return QV4::Value::undefinedValue(); + + QV8Engine *v8Engine = context->engine->v8Engine; + + QQmlPropertyData method; + + if (QQmlData *ddata = static_cast(QObjectPrivate::get(object)->declarativeData)) { + if (ddata->propertyCache) { + QQmlPropertyData *d = ddata->propertyCache->method(m_index); + if (!d) + return QV4::Value::undefinedValue(); + method = *d; + } + } + + if (method.coreIndex == -1) { + method.load(object->metaObject()->method(m_index)); + + if (method.coreIndex == -1) + return QV4::Value::undefinedValue(); + } + + if (method.isV4Function()) { + QV4::Value rv = QV4::Value::undefinedValue(); + + QQmlV4Function func(argc, args, &rv, m_qmlGlobal.value(), + QmlContextWrapper::getContext(m_qmlGlobal.value()), + v8Engine); + QQmlV4Function *funcptr = &func; + + void *args[] = { 0, &funcptr }; + QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, method.coreIndex, args); + + return rv; + } + + CallArgs callArgs(argc, args); + if (!method.isOverload()) { + return CallPrecise(object, method, v8Engine, callArgs); + } else { + return CallOverloaded(object, method, v8Engine, callArgs); + } +} + +DEFINE_MANAGED_VTABLE(QObjectMethod); + +QmlSignalHandler::QmlSignalHandler(ExecutionEngine *engine, QObject *object, int signalIndex) + : Object(engine) + , m_object(object) + , m_signalIndex(signalIndex) +{ + vtbl = &static_vtbl; + prototype = engine->objectPrototype; +} + +DEFINE_MANAGED_VTABLE(QmlSignalHandler); + +void MultiplyWrappedQObjectMap::insert(QObject *key, Object *value) +{ + QHash::insert(key, value); + connect(key, SIGNAL(destroyed(QObject*)), this, SLOT(removeDestroyedObject(QObject*))); +} + +MultiplyWrappedQObjectMap::Iterator MultiplyWrappedQObjectMap::erase(MultiplyWrappedQObjectMap::Iterator it) +{ + disconnect(it.key(), SIGNAL(destroyed(QObject*)), this, SLOT(removeDestroyedObject(QObject*))); + return QHash::erase(it); +} + +void MultiplyWrappedQObjectMap::remove(QObject *key) +{ + Iterator it = find(key); + if (it == end()) + return; + erase(it); +} + +void MultiplyWrappedQObjectMap::removeDestroyedObject(QObject *object) +{ + QHash::remove(object); +} + +QT_END_NAMESPACE + diff --git a/src/qml/jsruntime/qv4qobjectwrapper_p.h b/src/qml/jsruntime/qv4qobjectwrapper_p.h new file mode 100644 index 0000000000..6580d19fe9 --- /dev/null +++ b/src/qml/jsruntime/qv4qobjectwrapper_p.h @@ -0,0 +1,201 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4QOBJECTWRAPPER_P_H +#define QV4QOBJECTWRAPPER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +class QObject; +class QQmlData; +class QQmlPropertyCache; +class QQmlPropertyData; + +namespace QV4 { +struct QObjectSlotDispatcher; + +struct Q_QML_EXPORT QObjectWrapper : public QV4::Object +{ + Q_MANAGED + + enum RevisionMode { IgnoreRevision, CheckRevision }; + + static void initializeBindings(ExecutionEngine *engine); + + QObject *object() const { return m_object.data(); } + + Value getQmlProperty(ExecutionContext *ctx, QQmlContextData *qmlContext, String *name, RevisionMode revisionMode, bool *hasProperty = 0, bool includeImports = false); + static Value getQmlProperty(ExecutionContext *ctx, QQmlContextData *qmlContext, QObject *object, String *name, RevisionMode revisionMode, bool *hasProperty = 0); + + static bool setQmlProperty(ExecutionContext *ctx, QQmlContextData *qmlContext, QObject *object, String *name, RevisionMode revisionMode, const Value &value); + + static Value wrap(ExecutionEngine *engine, QObject *object); + + using Object::get; + +private: + static Value create(ExecutionEngine *engine, QQmlData *ddata, QObject *object); + + QObjectWrapper(ExecutionEngine *engine, QObject *object); + + QQmlPropertyData *findProperty(ExecutionEngine *engine, QQmlContextData *qmlContext, String *name, RevisionMode revisionMode, QQmlPropertyData *local) const; + + QPointer m_object; + String *m_destroy; + String *m_toString; + + static Value get(Managed *m, String *name, bool *hasProperty); + static void put(Managed *m, String *name, const Value &value); + static PropertyAttributes query(const Managed *, String *name); + static Property *advanceIterator(Managed *m, ObjectIterator *it, String **name, uint *index, PropertyAttributes *attributes); + static void markObjects(Managed *that); + static void collectDeletables(Managed *m, GCDeletable **deletable); + static void destroy(Managed *that) + { + static_cast(that)->~QObjectWrapper(); + } + + static Value method_connect(SimpleCallContext *ctx); + static Value method_disconnect(SimpleCallContext *ctx); +}; + +struct QObjectMethod : public QV4::FunctionObject +{ + Q_MANAGED + + enum { DestroyMethod = -1, ToStringMethod = -2 }; + + static Value create(QV4::ExecutionContext *scope, QObject *object, int index, const Value &qmlGlobal = Value::undefinedValue()); + + int methodIndex() const { return m_index; } + QObject *object() const { return m_object.data(); } + +private: + QObjectMethod(QV4::ExecutionContext *scope, QObject *object, int index, const QV4::Value &qmlGlobal); + + QV4::Value method_toString(QV4::ExecutionContext *ctx); + QV4::Value method_destroy(QV4::ExecutionContext *ctx, Value *args, int argc); + + QPointer m_object; + int m_index; + QV4::PersistentValue m_qmlGlobal; + + static Value call(Managed *, const Value &thisObject, Value *args, int argc); + + Value callInternal(const Value &, Value *args, int argc); + + static void destroy(Managed *that) + { + static_cast(that)->~QObjectMethod(); + } +}; + +struct QmlSignalHandler : public QV4::Object +{ + Q_MANAGED + + QmlSignalHandler(ExecutionEngine *engine, QObject *object, int signalIndex); + + int signalIndex() const { return m_signalIndex; } + QObject *object() const { return m_object.data(); } + +private: + QPointer m_object; + int m_signalIndex; + + static void destroy(Managed *that) + { + static_cast(that)->~QmlSignalHandler(); + } +}; + +class MultiplyWrappedQObjectMap : public QObject, + private QHash +{ + Q_OBJECT +public: + typedef QHash::ConstIterator ConstIterator; + typedef QHash::Iterator Iterator; + + ConstIterator begin() const { return QHash::constBegin(); } + Iterator begin() { return QHash::begin(); } + ConstIterator end() const { return QHash::constEnd(); } + Iterator end() { return QHash::end(); } + + void insert(QObject *key, Object *value); + Object *value(QObject *key) const { return QHash::value(key, 0); } + Iterator erase(Iterator it); + void remove(QObject *key); + +private slots: + void removeDestroyedObject(QObject*); +}; + +} + +QT_END_NAMESPACE + +#endif // QV4QOBJECTWRAPPER_P_H + + diff --git a/src/qml/jsruntime/qv4regexp.cpp b/src/qml/jsruntime/qv4regexp.cpp new file mode 100644 index 0000000000..ab01ce7650 --- /dev/null +++ b/src/qml/jsruntime/qv4regexp.cpp @@ -0,0 +1,180 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4regexp_p.h" + +#include "qv4engine_p.h" + +using namespace QV4; + +RegExpCache::~RegExpCache() +{ + for (RegExpCache::Iterator it = begin(), e = end(); + it != e; ++it) + it.value()->m_cache = 0; + clear(); +} + +DEFINE_MANAGED_VTABLE(RegExp); + +uint RegExp::match(const QString &string, int start, uint *matchOffsets) +{ + if (!isValid()) + return JSC::Yarr::offsetNoMatch; + + WTF::String s(string); + +#if ENABLE(YARR_JIT) + if (!m_jitCode.isFallBack() && m_jitCode.has16BitCode()) + return m_jitCode.execute(s.characters16(), start, s.length(), (int*)matchOffsets).start; +#endif + + return JSC::Yarr::interpret(m_byteCode.get(), s.characters16(), string.length(), start, matchOffsets); +} + +RegExp* RegExp::create(ExecutionEngine* engine, const QString& pattern, bool ignoreCase, bool multiline) +{ + RegExpCacheKey key(pattern, ignoreCase, multiline); + + RegExpCache *cache = engine->regExpCache; + if (cache) { + if (RegExp *result = cache->value(key)) + return result; + } + + RegExp *result = new (engine->memoryManager) RegExp(engine, pattern, ignoreCase, multiline); + + if (!cache) + cache = engine->regExpCache = new RegExpCache; + + result->m_cache = cache; + cache->insert(key, result); + + return result; +} + +RegExp::RegExp(ExecutionEngine* engine, const QString &pattern, bool ignoreCase, bool multiline) + : Managed(engine->emptyClass) + , m_pattern(pattern) + , m_cache(0) + , m_subPatternCount(0) + , m_ignoreCase(ignoreCase) + , m_multiLine(multiline) +{ + vtbl = &static_vtbl; + type = Type_RegExpObject; + + if (!engine) + return; + const char* error = 0; + JSC::Yarr::YarrPattern yarrPattern(WTF::String(pattern), ignoreCase, multiline, &error); + if (error) + return; + m_subPatternCount = yarrPattern.m_numSubpatterns; + m_byteCode = JSC::Yarr::byteCompile(yarrPattern, engine->bumperPointerAllocator); +#if ENABLE(YARR_JIT) + if (!yarrPattern.m_containsBackreferences && engine->iselFactory->jitCompileRegexps()) { + JSC::JSGlobalData dummy(engine->regExpAllocator); + JSC::Yarr::jitCompile(yarrPattern, JSC::Yarr::Char16, &dummy, m_jitCode); + } +#endif +} + +RegExp::~RegExp() +{ + if (m_cache) { + RegExpCacheKey key(this); + m_cache->remove(key); + } + _data = 0; +} + +void RegExp::destroy(Managed *that) +{ + static_cast(that)->~RegExp(); +} + +void RegExp::markObjects(Managed *that) +{ +} + +Value RegExp::get(Managed *m, String *name, bool *hasProperty) +{ + return Value::undefinedValue(); +} + +Value RegExp::getIndexed(Managed *m, uint index, bool *hasProperty) +{ + return Value::undefinedValue(); +} + +void RegExp::put(Managed *m, String *name, const Value &value) +{ +} + +void RegExp::putIndexed(Managed *m, uint index, const Value &value) +{ +} + +PropertyAttributes RegExp::query(const Managed *m, String *name) +{ + return Attr_Invalid; +} + +PropertyAttributes RegExp::queryIndexed(const Managed *m, uint index) +{ + return Attr_Invalid; +} + +bool RegExp::deleteProperty(Managed *, String *) +{ + return false; +} + +bool RegExp::deleteIndexedProperty(Managed *m, uint index) +{ + return false; +} + +Property *RegExp::advanceIterator(Managed *m, ObjectIterator *it, String **name, uint *index, PropertyAttributes *attributes) +{ + return 0; +} diff --git a/src/qml/jsruntime/qv4regexp_p.h b/src/qml/jsruntime/qv4regexp_p.h new file mode 100644 index 0000000000..6edbd4b2ad --- /dev/null +++ b/src/qml/jsruntime/qv4regexp_p.h @@ -0,0 +1,151 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4REGEXP_H +#define QV4REGEXP_H + +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include + +#include "qv4managed_p.h" +#include "qv4engine_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct ExecutionEngine; + +struct RegExpCacheKey +{ + RegExpCacheKey(const QString &pattern, bool ignoreCase, bool multiLine) + : pattern(pattern) + , ignoreCase(ignoreCase) + , multiLine(multiLine) + { } + explicit inline RegExpCacheKey(const RegExp *re); + + bool operator==(const RegExpCacheKey &other) const + { return pattern == other.pattern && ignoreCase == other.ignoreCase && multiLine == other.multiLine; } + bool operator!=(const RegExpCacheKey &other) const + { return !operator==(other); } + + QString pattern; + uint ignoreCase : 1; + uint multiLine : 1; +}; + +inline uint qHash(const RegExpCacheKey& key, uint seed = 0) Q_DECL_NOTHROW +{ return qHash(key.pattern, seed); } + +class RegExpCache : public QHash +{ +public: + ~RegExpCache(); +}; + +class RegExp : public Managed +{ + Q_MANAGED +public: + static RegExp* create(ExecutionEngine* engine, const QString& pattern, bool ignoreCase = false, bool multiline = false); + ~RegExp(); + + QString pattern() const { return m_pattern; } + + bool isValid() const { return m_byteCode.get(); } + + uint match(const QString& string, int start, uint *matchOffsets); + + bool ignoreCase() const { return m_ignoreCase; } + bool multiLine() const { return m_multiLine; } + int captureCount() const { return m_subPatternCount + 1; } + +protected: + static void destroy(Managed *that); + static void markObjects(Managed *that); + static Value get(Managed *m, String *name, bool *hasProperty); + static Value getIndexed(Managed *m, uint index, bool *hasProperty); + static void put(Managed *m, String *name, const Value &value); + static void putIndexed(Managed *m, uint index, const Value &value); + static PropertyAttributes query(const Managed *m, String *name); + static PropertyAttributes queryIndexed(const Managed *m, uint index); + static bool deleteProperty(Managed *, String *); + static bool deleteIndexedProperty(Managed *m, uint index); + static Property *advanceIterator(Managed *m, ObjectIterator *it, String **name, uint *index, PropertyAttributes *attributes); + +private: + friend class RegExpCache; + Q_DISABLE_COPY(RegExp); + RegExp(ExecutionEngine* engine, const QString& pattern, bool ignoreCase, bool multiline); + + const QString m_pattern; + OwnPtr m_byteCode; +#if ENABLE(YARR_JIT) + JSC::Yarr::YarrCodeBlock m_jitCode; +#endif + RegExpCache *m_cache; + int m_subPatternCount; + const bool m_ignoreCase; + const bool m_multiLine; +}; + +inline RegExpCacheKey::RegExpCacheKey(const RegExp *re) + : pattern(re->pattern()) + , ignoreCase(re->ignoreCase()) + , multiLine(re->multiLine()) +{} + + +} + +QT_END_NAMESPACE + +#endif // QV4REGEXP_H diff --git a/src/qml/jsruntime/qv4regexpobject.cpp b/src/qml/jsruntime/qv4regexpobject.cpp new file mode 100644 index 0000000000..0dc14e5722 --- /dev/null +++ b/src/qml/jsruntime/qv4regexpobject.cpp @@ -0,0 +1,359 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4regexpobject_p.h" +#include "qv4jsir_p.h" +#include "qv4isel_p.h" +#include "qv4objectproto_p.h" +#include "qv4stringobject_p.h" +#include "qv4mm_p.h" + +#include +#include +#include +#include +#include +#include +#include "private/qlocale_tools_p.h" + +#include +#include +#include +#include +#include +#include +#include "qv4alloca_p.h" + +QT_BEGIN_NAMESPACE + +Q_CORE_EXPORT QString qt_regexp_toCanonical(const QString &, QRegExp::PatternSyntax); + +using namespace QV4; + +DEFINE_MANAGED_VTABLE(RegExpObject); + +RegExpObject::RegExpObject(ExecutionEngine *engine, RegExp* value, bool global) + : Object(engine) + , value(value) + , global(global) +{ + init(engine); +} + +// Converts a QRegExp to a JS RegExp. +// The conversion is not 100% exact since ECMA regexp and QRegExp +// have different semantics/flags, but we try to do our best. +RegExpObject::RegExpObject(ExecutionEngine *engine, const QRegExp &re) + : Object(engine) + , value(0) + , global(false) +{ + // Convert the pattern to a ECMAScript pattern. + QString pattern = QT_PREPEND_NAMESPACE(qt_regexp_toCanonical)(re.pattern(), re.patternSyntax()); + if (re.isMinimal()) { + QString ecmaPattern; + int len = pattern.length(); + ecmaPattern.reserve(len); + int i = 0; + const QChar *wc = pattern.unicode(); + bool inBracket = false; + while (i < len) { + QChar c = wc[i++]; + ecmaPattern += c; + switch (c.unicode()) { + case '?': + case '+': + case '*': + case '}': + if (!inBracket) + ecmaPattern += QLatin1Char('?'); + break; + case '\\': + if (i < len) + ecmaPattern += wc[i++]; + break; + case '[': + inBracket = true; + break; + case ']': + inBracket = false; + break; + default: + break; + } + } + pattern = ecmaPattern; + } + + value = RegExp::create(engine, pattern, re.caseSensitivity() == Qt::CaseInsensitive, false); + + init(engine); +} + +void RegExpObject::init(ExecutionEngine *engine) +{ + vtbl = &static_vtbl; + type = Type_RegExpObject; + + Property *lastIndexProperty = insertMember(engine->newIdentifier(QStringLiteral("lastIndex")), + Attr_NotEnumerable|Attr_NotConfigurable); + lastIndexProperty->value = Value::fromInt32(0); + if (!this->value) + return; + + QString p = this->value->pattern(); + if (p.isEmpty()) { + p = QStringLiteral("(?:)"); + } else { + // escape certain parts, see ch. 15.10.4 + p.replace('/', QLatin1String("\\/")); + } + + defineReadonlyProperty(engine->newIdentifier(QStringLiteral("source")), Value::fromString(engine->newString(p))); + defineReadonlyProperty(engine->newIdentifier(QStringLiteral("global")), Value::fromBoolean(global)); + defineReadonlyProperty(engine->newIdentifier(QStringLiteral("ignoreCase")), Value::fromBoolean(this->value->ignoreCase())); + defineReadonlyProperty(engine->newIdentifier(QStringLiteral("multiline")), Value::fromBoolean(this->value->multiLine())); +} + + +void RegExpObject::destroy(Managed *that) +{ + static_cast(that)->~RegExpObject(); +} + +void RegExpObject::markObjects(Managed *that) +{ + RegExpObject *re = static_cast(that); + if (re->value) + re->value->mark(); + Object::markObjects(that); +} + +Property *RegExpObject::lastIndexProperty(ExecutionContext *ctx) +{ + assert(0 == internalClass->find(ctx->engine->newIdentifier(QStringLiteral("lastIndex")))); + return &memberData[0]; +} + +// Converts a JS RegExp to a QRegExp. +// The conversion is not 100% exact since ECMA regexp and QRegExp +// have different semantics/flags, but we try to do our best. +QRegExp RegExpObject::toQRegExp() const +{ + Qt::CaseSensitivity caseSensitivity = value->ignoreCase() ? Qt::CaseInsensitive : Qt::CaseSensitive; + return QRegExp(value->pattern(), caseSensitivity, QRegExp::RegExp2); +} + +QString RegExpObject::toString() const +{ + QString result = QChar('/') + source(); + result += QChar('/'); + if (global) + result += QChar('g'); + if (value->ignoreCase()) + result += QChar('i'); + if (value->multiLine()) + result += QChar('m'); + return result; +} + +QString RegExpObject::source() const +{ + return const_cast(this)->get(internalClass->engine->newIdentifier(QStringLiteral("source"))).stringValue()->toQString(); +} + +uint RegExpObject::flags() const +{ + uint f = 0; + if (global) + f |= QV4::RegExpObject::RegExp_Global; + if (value->ignoreCase()) + f |= QV4::RegExpObject::RegExp_IgnoreCase; + if (value->multiLine()) + f |= QV4::RegExpObject::RegExp_Multiline; + return f; +} + +DEFINE_MANAGED_VTABLE(RegExpCtor); + +RegExpCtor::RegExpCtor(ExecutionContext *scope) + : FunctionObject(scope, scope->engine->newIdentifier(QStringLiteral("RegExp"))) +{ + vtbl = &static_vtbl; +} + +Value RegExpCtor::construct(Managed *m, Value *argv, int argc) +{ + Value r = argc > 0 ? argv[0] : Value::undefinedValue(); + Value f = argc > 1 ? argv[1] : Value::undefinedValue(); + ExecutionContext *ctx = m->engine()->current; + if (RegExpObject *re = r.as()) { + if (!f.isUndefined()) + ctx->throwTypeError(); + + RegExpObject *o = ctx->engine->newRegExpObject(re->value, re->global); + return Value::fromObject(o); + } + + QString pattern; + if (!r.isUndefined()) + pattern = r.toString(ctx)->toQString(); + + bool global = false; + bool ignoreCase = false; + bool multiLine = false; + if (!f.isUndefined()) { + f = __qmljs_to_string(f, ctx); + QString str = f.stringValue()->toQString(); + for (int i = 0; i < str.length(); ++i) { + if (str.at(i) == QChar('g') && !global) { + global = true; + } else if (str.at(i) == QChar('i') && !ignoreCase) { + ignoreCase = true; + } else if (str.at(i) == QChar('m') && !multiLine) { + multiLine = true; + } else { + ctx->throwSyntaxError(0); + } + } + } + + RegExp* re = RegExp::create(ctx->engine, pattern, ignoreCase, multiLine); + if (!re->isValid()) + ctx->throwSyntaxError(0); + + RegExpObject *o = ctx->engine->newRegExpObject(re, global); + return Value::fromObject(o); +} + +Value RegExpCtor::call(Managed *that, const Value &, Value *argv, int argc) +{ + if (argc > 0 && argv[0].as()) { + if (argc == 1 || argv[1].isUndefined()) + return argv[0]; + } + + return construct(that, argv, argc); +} + +void RegExpPrototype::init(ExecutionContext *ctx, const Value &ctor) +{ + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(2)); + defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); + defineDefaultProperty(ctx, QStringLiteral("exec"), method_exec, 1); + defineDefaultProperty(ctx, QStringLiteral("test"), method_test, 1); + defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); + defineDefaultProperty(ctx, QStringLiteral("compile"), method_compile, 2); +} + +Value RegExpPrototype::method_exec(SimpleCallContext *ctx) +{ + RegExpObject *r = ctx->thisObject.as(); + if (!r) + ctx->throwTypeError(); + + Value arg = ctx->argument(0); + arg = __qmljs_to_string(arg, ctx); + QString s = arg.stringValue()->toQString(); + + int offset = r->global ? r->lastIndexProperty(ctx)->value.toInt32() : 0; + if (offset < 0 || offset > s.length()) { + r->lastIndexProperty(ctx)->value = Value::fromInt32(0); + return Value::nullValue(); + } + + uint* matchOffsets = (uint*)alloca(r->value->captureCount() * 2 * sizeof(uint)); + int result = r->value->match(s, offset, matchOffsets); + if (result == -1) { + r->lastIndexProperty(ctx)->value = Value::fromInt32(0); + return Value::nullValue(); + } + + // fill in result data + ArrayObject *array = ctx->engine->newArrayObject(); + for (int i = 0; i < r->value->captureCount(); ++i) { + int start = matchOffsets[i * 2]; + int end = matchOffsets[i * 2 + 1]; + Value entry = Value::undefinedValue(); + if (start != -1 && end != -1) + entry = Value::fromString(ctx, s.mid(start, end - start)); + array->push_back(entry); + } + + array->put(ctx, QLatin1String("index"), Value::fromInt32(result)); + array->put(ctx, QLatin1String("input"), arg); + + if (r->global) + r->lastIndexProperty(ctx)->value = Value::fromInt32(matchOffsets[1]); + + return Value::fromObject(array); +} + +Value RegExpPrototype::method_test(SimpleCallContext *ctx) +{ + Value r = method_exec(ctx); + return Value::fromBoolean(!r.isNull()); +} + +Value RegExpPrototype::method_toString(SimpleCallContext *ctx) +{ + RegExpObject *r = ctx->thisObject.as(); + if (!r) + ctx->throwTypeError(); + + return Value::fromString(ctx, r->toString()); +} + +Value RegExpPrototype::method_compile(SimpleCallContext *ctx) +{ + RegExpObject *r = ctx->thisObject.as(); + if (!r) + ctx->throwTypeError(); + + RegExpObject *re = ctx->engine->regExpCtor.asFunctionObject()->construct(ctx->arguments, ctx->argumentCount).as(); + + r->value = re->value; + r->global = re->global; + return Value::undefinedValue(); +} + +QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4regexpobject_p.h b/src/qml/jsruntime/qv4regexpobject_p.h new file mode 100644 index 0000000000..0b9b7122a9 --- /dev/null +++ b/src/qml/jsruntime/qv4regexpobject_p.h @@ -0,0 +1,123 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4REGEXPOBJECT_H +#define QV4REGEXPOBJECT_H + +#include "qv4runtime_p.h" +#include "qv4engine_p.h" +#include "qv4context_p.h" +#include "qv4functionobject_p.h" +#include "qv4string_p.h" +#include "qv4codegen_p.h" +#include "qv4isel_p.h" +#include "qv4managed_p.h" +#include "qv4property_p.h" +#include "qv4objectiterator_p.h" +#include "qv4regexp_p.h" + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct RegExp; + +struct RegExpObject: Object { + Q_MANAGED + // needs to be compatible with the flags in qv4jsir_p.h + enum Flags { + RegExp_Global = 0x01, + RegExp_IgnoreCase = 0x02, + RegExp_Multiline = 0x04 + }; + + RegExp* value; + Property *lastIndexProperty(ExecutionContext *ctx); + bool global; + RegExpObject(ExecutionEngine *engine, RegExp* value, bool global); + RegExpObject(ExecutionEngine *engine, const QRegExp &re); + ~RegExpObject() {} + + void init(ExecutionEngine *engine); + + QRegExp toQRegExp() const; + QString toString() const; + QString source() const; + uint flags() const; + +protected: + static void destroy(Managed *that); + static void markObjects(Managed *that); +}; + + +struct RegExpCtor: FunctionObject +{ + RegExpCtor(ExecutionContext *scope); + + static Value construct(Managed *m, Value *args, int argc); + static Value call(Managed *that, const Value &, Value *, int); + +protected: + static const ManagedVTable static_vtbl; +}; + +struct RegExpPrototype: RegExpObject +{ + RegExpPrototype(ExecutionEngine* engine): RegExpObject(engine, RegExp::create(engine, QString()), false) {} + void init(ExecutionContext *ctx, const Value &ctor); + + static Value method_exec(SimpleCallContext *ctx); + static Value method_test(SimpleCallContext *ctx); + static Value method_toString(SimpleCallContext *ctx); + static Value method_compile(SimpleCallContext *ctx); +}; + +} + +QT_END_NAMESPACE + +#endif // QMLJS_OBJECTS_H diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp new file mode 100644 index 0000000000..ed2c1468ee --- /dev/null +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -0,0 +1,1250 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4global_p.h" +#include "qv4runtime_p.h" +#include "qv4object_p.h" +#include "qv4jsir_p.h" +#include "qv4objectproto_p.h" +#include "qv4globalobject_p.h" +#include "qv4stringobject_p.h" +#include "qv4lookup_p.h" +#include "qv4function_p.h" +#include "qv4exception_p.h" +#include "private/qlocale_tools_p.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "../../../3rdparty/double-conversion/double-conversion.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +void __qmljs_numberToString(QString *result, double num, int radix) +{ + Q_ASSERT(result); + + if (std::isnan(num)) { + *result = QStringLiteral("NaN"); + return; + } else if (qIsInf(num)) { + *result = QLatin1String(num < 0 ? "-Infinity" : "Infinity"); + return; + } + + if (radix == 10) { + char str[100]; + double_conversion::StringBuilder builder(str, sizeof(str)); + double_conversion::DoubleToStringConverter::EcmaScriptConverter().ToShortest(num, &builder); + *result = QString::fromLatin1(builder.Finalize()); + return; + } + + result->clear(); + bool negative = false; + + if (num < 0) { + negative = true; + num = -num; + } + + double frac = num - ::floor(num); + num = Value::toInteger(num); + + do { + char c = (char)::fmod(num, radix); + c = (c < 10) ? (c + '0') : (c - 10 + 'a'); + result->prepend(QLatin1Char(c)); + num = ::floor(num / radix); + } while (num != 0); + + if (frac != 0) { + result->append(QLatin1Char('.')); + do { + frac = frac * radix; + char c = (char)::floor(frac); + c = (c < 10) ? (c + '0') : (c - 10 + 'a'); + result->append(QLatin1Char(c)); + frac = frac - ::floor(frac); + } while (frac != 0); + } + + if (negative) + result->prepend(QLatin1Char('-')); +} + +void __qmljs_init_closure(ExecutionContext *ctx, Value *result, Function *clos) +{ + assert(clos); + *result = Value::fromObject(ctx->engine->newScriptFunction(ctx, clos)); +} + +Function *__qmljs_register_function(ExecutionContext *ctx, String *name, + bool hasDirectEval, + bool usesArgumentsObject, bool isStrict, + bool hasNestedFunctions, + String **formals, unsigned formalCount, + String **locals, unsigned localCount) +{ + Function *f = ctx->engine->newFunction(name ? name->toQString() : QString()); + + f->hasDirectEval = hasDirectEval; + f->usesArgumentsObject = usesArgumentsObject; + f->isStrict = isStrict; + f->hasNestedFunctions = hasNestedFunctions; + + for (unsigned i = 0; i < formalCount; ++i) + if (formals[i]) + f->formals.append(formals[i]); + for (unsigned i = 0; i < localCount; ++i) + if (locals[i]) + f->locals.append(locals[i]); + + return f; +} + +void __qmljs_delete_subscript(ExecutionContext *ctx, Value *result, const Value &base, const Value &index) +{ + if (Object *o = base.asObject()) { + uint n = index.asArrayIndex(); + if (n < UINT_MAX) { + Value res = Value::fromBoolean(o->deleteIndexedProperty(n)); + if (result) + *result = res; + return; + } + } + + String *name = index.toString(ctx); + __qmljs_delete_member(ctx, result, base, name); +} + +void __qmljs_delete_member(ExecutionContext *ctx, Value *result, const Value &base, String *name) +{ + Object *obj = base.toObject(ctx); + Value res = Value::fromBoolean(obj->deleteProperty(name)); + if (result) + *result = res; +} + +void __qmljs_delete_name(ExecutionContext *ctx, Value *result, String *name) +{ + Value res = Value::fromBoolean(ctx->deleteProperty(name)); + if (result) + *result = res; +} + +void __qmljs_add_helper(ExecutionContext *ctx, Value *result, const Value &left, const Value &right) +{ + Value pleft = __qmljs_to_primitive(left, PREFERREDTYPE_HINT); + Value pright = __qmljs_to_primitive(right, PREFERREDTYPE_HINT); + if (pleft.isString() || pright.isString()) { + if (!pleft.isString()) + pleft = __qmljs_to_string(pleft, ctx); + if (!pright.isString()) + pright = __qmljs_to_string(pright, ctx); + String *string = __qmljs_string_concat(ctx, pleft.stringValue(), pright.stringValue()); + *result = Value::fromString(string); + return; + } + double x = __qmljs_to_number(pleft); + double y = __qmljs_to_number(pright); + *result = Value::fromDouble(x + y); +} + +void __qmljs_instanceof(ExecutionContext *ctx, Value *result, const Value &left, const Value &right) +{ + Object *o = right.asObject(); + if (!o) + ctx->throwTypeError(); + + bool r = o->hasInstance(left); + *result = Value::fromBoolean(r); +} + +void __qmljs_in(ExecutionContext *ctx, Value *result, const Value &left, const Value &right) +{ + if (!right.isObject()) + ctx->throwTypeError(); + String *s = left.toString(ctx); + bool r = right.objectValue()->__hasProperty__(s); + *result = Value::fromBoolean(r); +} + +void __qmljs_inplace_bit_and_name(ExecutionContext *ctx, String *name, const Value &value) +{ + ctx->inplaceBitOp(name, value, __qmljs_bit_and); +} + +void __qmljs_inplace_bit_or_name(ExecutionContext *ctx, String *name, const Value &value) +{ + ctx->inplaceBitOp(name, value, __qmljs_bit_or); +} + +void __qmljs_inplace_bit_xor_name(ExecutionContext *ctx, String *name, const Value &value) +{ + ctx->inplaceBitOp(name, value, __qmljs_bit_xor); +} + +void __qmljs_inplace_add_name(ExecutionContext *ctx, String *name, const Value &value) +{ + Value lhs = ctx->getProperty(name); + Value result; + __qmljs_add(ctx, &result, lhs, value); + ctx->setProperty(name, result); +} + +void __qmljs_inplace_sub_name(ExecutionContext *ctx, String *name, const Value &value) +{ + ctx->inplaceBitOp(name, value, __qmljs_sub); +} + +void __qmljs_inplace_mul_name(ExecutionContext *ctx, String *name, const Value &value) +{ + ctx->inplaceBitOp(name, value, __qmljs_mul); +} + +void __qmljs_inplace_div_name(ExecutionContext *ctx, String *name, const Value &value) +{ + ctx->inplaceBitOp(name, value, __qmljs_div); +} + +void __qmljs_inplace_mod_name(ExecutionContext *ctx, String *name, const Value &value) +{ + ctx->inplaceBitOp(name, value, __qmljs_mod); +} + +void __qmljs_inplace_shl_name(ExecutionContext *ctx, String *name, const Value &value) +{ + ctx->inplaceBitOp(name, value, __qmljs_shl); +} + +void __qmljs_inplace_shr_name(ExecutionContext *ctx, String *name, const Value &value) +{ + ctx->inplaceBitOp(name, value, __qmljs_shr); +} + +void __qmljs_inplace_ushr_name(ExecutionContext *ctx, String *name, const Value &value) +{ + ctx->inplaceBitOp(name, value, __qmljs_ushr); +} + +void __qmljs_inplace_bit_and_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs) +{ + Object *obj = base.toObject(ctx); + obj->inplaceBinOp(ctx, __qmljs_bit_and, index, rhs); +} + +void __qmljs_inplace_bit_or_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs) +{ + Object *obj = base.toObject(ctx); + obj->inplaceBinOp(ctx, __qmljs_bit_or, index, rhs); +} + +void __qmljs_inplace_bit_xor_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs) +{ + Object *obj = base.toObject(ctx); + obj->inplaceBinOp(ctx, __qmljs_bit_xor, index, rhs); +} + +void __qmljs_inplace_add_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs) +{ + Object *obj = base.toObject(ctx); + obj->inplaceBinOp(ctx, __qmljs_add, index, rhs); +} + +void __qmljs_inplace_sub_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs) +{ + Object *obj = base.toObject(ctx); + obj->inplaceBinOp(ctx, __qmljs_sub, index, rhs); +} + +void __qmljs_inplace_mul_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs) +{ + Object *obj = base.toObject(ctx); + obj->inplaceBinOp(ctx, __qmljs_mul, index, rhs); +} + +void __qmljs_inplace_div_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs) +{ + Object *obj = base.toObject(ctx); + obj->inplaceBinOp(ctx, __qmljs_div, index, rhs); +} + +void __qmljs_inplace_mod_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs) +{ + Object *obj = base.toObject(ctx); + obj->inplaceBinOp(ctx, __qmljs_mod, index, rhs); +} + +void __qmljs_inplace_shl_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs) +{ + Object *obj = base.toObject(ctx); + obj->inplaceBinOp(ctx, __qmljs_shl, index, rhs); +} + +void __qmljs_inplace_shr_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs) +{ + Object *obj = base.toObject(ctx); + obj->inplaceBinOp(ctx, __qmljs_shr, index, rhs); +} + +void __qmljs_inplace_ushr_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs) +{ + Object *obj = base.toObject(ctx); + obj->inplaceBinOp(ctx, __qmljs_ushr, index, rhs); +} + +void __qmljs_inplace_bit_and_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs) +{ + Object *o = base.toObject(ctx); + o->inplaceBinOp(ctx, __qmljs_bit_and, name, rhs); +} + +void __qmljs_inplace_bit_or_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs) +{ + Object *o = base.toObject(ctx); + o->inplaceBinOp(ctx, __qmljs_bit_or, name, rhs); +} + +void __qmljs_inplace_bit_xor_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs) +{ + Object *o = base.toObject(ctx); + o->inplaceBinOp(ctx, __qmljs_bit_xor, name, rhs); +} + +void __qmljs_inplace_add_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs) +{ + Object *o = base.toObject(ctx); + o->inplaceBinOp(ctx, __qmljs_add, name, rhs); +} + +void __qmljs_inplace_sub_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs) +{ + Object *o = base.toObject(ctx); + o->inplaceBinOp(ctx, __qmljs_sub, name, rhs); +} + +void __qmljs_inplace_mul_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs) +{ + Object *o = base.toObject(ctx); + o->inplaceBinOp(ctx, __qmljs_mul, name, rhs); +} + +void __qmljs_inplace_div_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs) +{ + Object *o = base.toObject(ctx); + o->inplaceBinOp(ctx, __qmljs_div, name, rhs); +} + +void __qmljs_inplace_mod_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs) +{ + Object *o = base.toObject(ctx); + o->inplaceBinOp(ctx, __qmljs_mod, name, rhs); +} + +void __qmljs_inplace_shl_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs) +{ + Object *o = base.toObject(ctx); + o->inplaceBinOp(ctx, __qmljs_shl, name, rhs); +} + +void __qmljs_inplace_shr_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs) +{ + Object *o = base.toObject(ctx); + o->inplaceBinOp(ctx, __qmljs_shr, name, rhs); +} + +void __qmljs_inplace_ushr_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs) +{ + Object *o = base.toObject(ctx); + o->inplaceBinOp(ctx, __qmljs_ushr, name, rhs); +} + +double __qmljs_string_to_number(const QString &string) +{ + QString s = string.trimmed(); + if (s.startsWith(QLatin1String("0x")) || s.startsWith(QLatin1String("0X"))) + return s.toLong(0, 16); + bool ok; + QByteArray ba = s.toLatin1(); + const char *begin = ba.constData(); + const char *end = 0; + double d = qstrtod(begin, &end, &ok); + if (end - begin != ba.size()) { + if (ba == "Infinity" || ba == "+Infinity") + d = Q_INFINITY; + else if (ba == "-Infinity") + d = -Q_INFINITY; + else + d = std::numeric_limits::quiet_NaN(); + } + return d; +} + +Value __qmljs_string_from_number(ExecutionContext *ctx, double number) +{ + QString qstr; + __qmljs_numberToString(&qstr, number, 10); + String *string = ctx->engine->newString(qstr); + return Value::fromString(string); +} + +String *__qmljs_string_concat(ExecutionContext *ctx, String *first, String *second) +{ + const QString &a = first->toQString(); + const QString &b = second->toQString(); + QString newStr(a.length() + b.length(), Qt::Uninitialized); + QChar *data = newStr.data(); + memcpy(data, a.constData(), a.length()*sizeof(QChar)); + data += a.length(); + memcpy(data, b.constData(), b.length()*sizeof(QChar)); + + return ctx->engine->newString(newStr); +} + +Value __qmljs_object_default_value(Object *object, int typeHint) +{ + if (typeHint == PREFERREDTYPE_HINT) { + if (object->asDateObject()) + typeHint = STRING_HINT; + else + typeHint = NUMBER_HINT; + } + + ExecutionEngine *engine = object->internalClass->engine; + String *meth1 = engine->newString("toString"); + String *meth2 = engine->newString("valueOf"); + + if (typeHint == NUMBER_HINT) + qSwap(meth1, meth2); + + ExecutionContext *ctx = engine->current; + + Value conv = object->get(meth1); + if (FunctionObject *o = conv.asFunctionObject()) { + Value r = o->call(Value::fromObject(object), 0, 0); + if (r.isPrimitive()) + return r; + } + + conv = object->get(meth2); + if (FunctionObject *o = conv.asFunctionObject()) { + Value r = o->call(Value::fromObject(object), 0, 0); + if (r.isPrimitive()) + return r; + } + + ctx->throwTypeError(); + return Value::undefinedValue(); +} + +Bool __qmljs_to_boolean(const Value &value) +{ + return value.toBoolean(); +} + + +Object *__qmljs_convert_to_object(ExecutionContext *ctx, const Value &value) +{ + assert(!value.isObject()); + switch (value.type()) { + case Value::Undefined_Type: + case Value::Null_Type: + ctx->throwTypeError(); + case Value::Boolean_Type: + return ctx->engine->newBooleanObject(value); + case Value::String_Type: + return ctx->engine->newStringObject(value); + break; + case Value::Object_Type: + Q_UNREACHABLE(); + case Value::Integer_Type: + default: // double + return ctx->engine->newNumberObject(value); + } +} + +String *__qmljs_convert_to_string(ExecutionContext *ctx, const Value &value) +{ + switch (value.type()) { + case Value::Undefined_Type: + return ctx->engine->id_undefined; + case Value::Null_Type: + return ctx->engine->id_null; + case Value::Boolean_Type: + if (value.booleanValue()) + return ctx->engine->id_true; + else + return ctx->engine->id_false; + case Value::String_Type: + return value.stringValue(); + case Value::Object_Type: { + Value prim = __qmljs_to_primitive(value, STRING_HINT); + if (prim.isPrimitive()) + return __qmljs_convert_to_string(ctx, prim); + else + ctx->throwTypeError(); + } + case Value::Integer_Type: + return __qmljs_string_from_number(ctx, value.int_32).stringValue(); + default: // double + return __qmljs_string_from_number(ctx, value.doubleValue()).stringValue(); + } // switch +} + +void __qmljs_set_property(ExecutionContext *ctx, const Value &object, String *name, const Value &value) +{ + Object *o = object.toObject(ctx); + o->put(name, value); +} + +void __qmljs_get_element(ExecutionContext *ctx, Value *result, const Value &object, const Value &index) +{ + uint idx = index.asArrayIndex(); + + Object *o = object.asObject(); + if (!o) { + if (idx < UINT_MAX) { + if (String *str = object.asString()) { + if (idx >= (uint)str->toQString().length()) { + if (result) + *result = Value::undefinedValue(); + return; + } + const QString s = str->toQString().mid(idx, 1); + if (result) + *result = Value::fromString(ctx, s); + return; + } + } + + if (object.isNull() || object.isUndefined()) { + QString message = QStringLiteral("Cannot read property '%1' of %2").arg(index.toQString()).arg(object.toQString()); + ctx->throwTypeError(message); + } + + o = __qmljs_convert_to_object(ctx, object); + } + + if (idx < UINT_MAX) { + uint pidx = o->propertyIndexFromArrayIndex(idx); + if (pidx < UINT_MAX) { + if (!o->arrayAttributes || o->arrayAttributes[pidx].isData()) { + if (result) + *result = o->arrayData[pidx].value; + return; + } + } + + Value res = o->getIndexed(idx); + if (result) + *result = res; + return; + } + + String *name = index.toString(ctx); + Value res = o->get(name); + if (result) + *result = res; +} + +void __qmljs_set_element(ExecutionContext *ctx, const Value &object, const Value &index, const Value &value) +{ + Object *o = object.toObject(ctx); + + uint idx = index.asArrayIndex(); + if (idx < UINT_MAX) { + uint pidx = o->propertyIndexFromArrayIndex(idx); + if (pidx < UINT_MAX) { + if (o->arrayAttributes && !o->arrayAttributes[pidx].isEmpty() && !o->arrayAttributes[pidx].isWritable()) { + if (ctx->strictMode) + ctx->throwTypeError(); + return; + } + + Property *p = o->arrayData + pidx; + if (!o->arrayAttributes || o->arrayAttributes[pidx].isData()) { + p->value = value; + return; + } + + if (o->arrayAttributes[pidx].isAccessor()) { + FunctionObject *setter = p->setter(); + if (!setter) { + if (ctx->strictMode) + ctx->throwTypeError(); + return; + } + + Value args[1]; + args[0] = value; + setter->call(Value::fromObject(o), args, 1); + return; + } + } + o->putIndexed(idx, value); + return; + } + + String *name = index.toString(ctx); + o->put(name, value); +} + +void __qmljs_foreach_iterator_object(ExecutionContext *ctx, Value *result, const Value &in) +{ + Object *o = 0; + if (!in.isNull() && !in.isUndefined()) + o = in.toObject(ctx); + Object *it = ctx->engine->newForEachIteratorObject(ctx, o); + *result = Value::fromObject(it); +} + +void __qmljs_foreach_next_property_name(Value *result, const Value &foreach_iterator) +{ + assert(foreach_iterator.isObject()); + + ForEachIteratorObject *it = static_cast(foreach_iterator.objectValue()); + assert(it->as()); + + *result = it->nextPropertyName(); +} + + +void __qmljs_set_activation_property(ExecutionContext *ctx, String *name, const Value &value) +{ + ctx->setProperty(name, value); +} + +void __qmljs_get_property(ExecutionContext *ctx, Value *result, const Value &object, String *name) +{ + Value res; + Managed *m = object.asManaged(); + if (m) { + res = m->get(name); + } else { + if (object.isNull() || object.isUndefined()) { + QString message = QStringLiteral("Cannot read property '%1' of %2").arg(name->toQString()).arg(object.toQString()); + ctx->throwTypeError(message); + } + + m = __qmljs_convert_to_object(ctx, object); + res = m->get(name); + } + if (result) + *result = res; +} + +void __qmljs_get_activation_property(ExecutionContext *ctx, Value *result, String *name) +{ + *result = ctx->getProperty(name); +} + +uint __qmljs_equal_helper(const Value &x, const Value &y) +{ + Q_ASSERT(x.type() != y.type()); + + if (x.isNumber() && y.isNumber()) + return x.asDouble() == y.asDouble(); + if (x.isNull() && y.isUndefined()) { + return true; + } else if (x.isUndefined() && y.isNull()) { + return true; + } else if (x.isNumber() && y.isString()) { + double dy = __qmljs_to_number(y); + return x.asDouble() == dy; + } else if (x.isString() && y.isNumber()) { + double dx = __qmljs_to_number(x); + return dx == y.asDouble(); + } else if (x.isBoolean()) { + Value nx = Value::fromDouble((double) x.booleanValue()); + return __qmljs_cmp_eq(nx, y); + } else if (y.isBoolean()) { + Value ny = Value::fromDouble((double) y.booleanValue()); + return __qmljs_cmp_eq(x, ny); + } else if ((x.isNumber() || x.isString()) && y.isObject()) { + Value py = __qmljs_to_primitive(y, PREFERREDTYPE_HINT); + return __qmljs_cmp_eq(x, py); + } else if (x.isObject() && (y.isNumber() || y.isString())) { + Value px = __qmljs_to_primitive(x, PREFERREDTYPE_HINT); + return __qmljs_cmp_eq(px, y); + } + + return false; +} + +Bool __qmljs_strict_equal(const Value &x, const Value &y) +{ + TRACE2(x, y); + + if (x.rawValue() == y.rawValue()) + // NaN != NaN + return (x.tag & QV4::Value::NotDouble_Mask) != QV4::Value::NaN_Mask; + + if (x.isNumber()) + return y.isNumber() && x.asDouble() == y.asDouble(); + if (x.isString()) + return y.isString() && x.stringValue()->isEqualTo(y.stringValue()); + return false; +} + + +void __qmljs_call_global_lookup(ExecutionContext *context, Value *result, uint index, Value *args, int argc) +{ + Lookup *l = context->lookups + index; + Value v; + l->globalGetter(l, context, &v); + FunctionObject *o = v.asFunctionObject(); + if (!o) + context->throwTypeError(); + + Value thisObject = Value::undefinedValue(); + + if (o == context->engine->evalFunction && l->name->isEqualTo(context->engine->id_eval)) { + Value res = static_cast(o)->evalCall(thisObject, args, argc, true); + if (result) + *result = res; + return; + } + + Value res = o->call(thisObject, args, argc); + if (result) + *result = res; +} + + +void __qmljs_call_activation_property(ExecutionContext *context, Value *result, String *name, Value *args, int argc) +{ + Object *base; + Value func = context->getPropertyAndBase(name, &base); + FunctionObject *o = func.asFunctionObject(); + if (!o) { + QString objectAsString = QStringLiteral("[null]"); + if (base) + objectAsString = Value::fromObject(base).toQString(); + QString msg = QStringLiteral("Property '%1' of object %2 is not a function").arg(name->toQString()).arg(objectAsString); + context->throwTypeError(msg); + } + + Value thisObject = base ? Value::fromObject(base) : Value::undefinedValue(); + + if (o == context->engine->evalFunction && name->isEqualTo(context->engine->id_eval)) { + Value res = static_cast(o)->evalCall(thisObject, args, argc, true); + if (result) + *result = res; + return; + } + + Value res = o->call(thisObject, args, argc); + if (result) + *result = res; +} + +void __qmljs_call_property(ExecutionContext *context, Value *result, const Value &thatObject, String *name, Value *args, int argc) +{ + Value thisObject = thatObject; + Managed *baseObject = thisObject.asManaged(); + if (!baseObject) { + if (thisObject.isNull() || thisObject.isUndefined()) { + QString message = QStringLiteral("Cannot call method '%1' of %2").arg(name->toQString()).arg(thisObject.toQString()); + context->throwTypeError(message); + } + + baseObject = __qmljs_convert_to_object(context, thisObject); + thisObject = Value::fromObject(static_cast(baseObject)); + } + + Value func = baseObject->get(name); + FunctionObject *o = func.asFunctionObject(); + if (!o) { + QString error = QString("Property '%1' of object %2 is not a function").arg(name->toQString(), thisObject.toQString()); + context->throwTypeError(error); + } + + Value res = o->call(thisObject, args, argc); + if (result) + *result = res; +} + +void __qmljs_call_property_lookup(ExecutionContext *context, Value *result, const Value &thisObject, uint index, Value *args, int argc) +{ + Lookup *l = context->lookups + index; + + Object *baseObject; + if (thisObject.isObject()) + baseObject = thisObject.objectValue(); + else if (thisObject.isString()) + baseObject = context->engine->stringPrototype; + else + baseObject = __qmljs_convert_to_object(context, thisObject); + + PropertyAttributes attrs; + Property *p = l->lookup(baseObject, &attrs); + if (!p) + context->throwTypeError(); + Value func = attrs.isData() ? p->value : baseObject->getValue(p, attrs); + FunctionObject *o = func.asFunctionObject(); + if (!o) + context->throwTypeError(); + + Value res = o->call(thisObject, args, argc); + if (result) + *result = res; +} + +void __qmljs_call_element(ExecutionContext *context, Value *result, const Value &that, const Value &index, Value *args, int argc) +{ + Object *baseObject = that.toObject(context); + Value thisObject = Value::fromObject(baseObject); + + Value func = baseObject->get(index.toString(context)); + Object *o = func.asObject(); + if (!o) + context->throwTypeError(); + + Value res = o->call(thisObject, args, argc); + if (result) + *result = res; +} + +void __qmljs_call_value(ExecutionContext *context, Value *result, const Value *thisObject, const Value &func, Value *args, int argc) +{ + Object *o = func.asObject(); + if (!o) + context->throwTypeError(); + Value res = o->call(thisObject ? *thisObject : Value::undefinedValue(), args, argc); + if (result) + *result = res; +} + + +void __qmljs_construct_global_lookup(ExecutionContext *context, Value *result, uint index, Value *args, int argc) +{ + Lookup *l = context->lookups + index; + Value func; + l->globalGetter(l, context, &func); + + if (Object *f = func.asObject()) { + Value res = f->construct(args, argc); + if (result) + *result = res; + return; + } + + context->throwTypeError(); +} + + +void __qmljs_construct_activation_property(ExecutionContext *context, Value *result, String *name, Value *args, int argc) +{ + Value func = context->getProperty(name); + __qmljs_construct_value(context, result, func, args, argc); +} + +void __qmljs_construct_value(ExecutionContext *context, Value *result, const Value &func, Value *args, int argc) +{ + if (Object *f = func.asObject()) { + Value res = f->construct(args, argc); + if (result) + *result = res; + return; + } + + context->throwTypeError(); +} + +void __qmljs_construct_property(ExecutionContext *context, Value *result, const Value &base, String *name, Value *args, int argc) +{ + Object *thisObject = base.toObject(context); + + Value func = thisObject->get(name); + if (Object *f = func.asObject()) { + Value res = f->construct(args, argc); + if (result) + *result = res; + return; + } + + context->throwTypeError(); +} + +void __qmljs_throw(ExecutionContext *context, const Value &value) +{ + Exception::throwException(context, value); +} + +void __qmljs_builtin_typeof(ExecutionContext *ctx, Value *result, const Value &value) +{ + if (!result) + return; + String *res = 0; + switch (value.type()) { + case Value::Undefined_Type: + res = ctx->engine->id_undefined; + break; + case Value::Null_Type: + res = ctx->engine->id_object; + break; + case Value::Boolean_Type: + res = ctx->engine->id_boolean; + break; + case Value::String_Type: + res = ctx->engine->id_string; + break; + case Value::Object_Type: + if (value.objectValue()->asFunctionObject()) + res = ctx->engine->id_function; + else + res = ctx->engine->id_object; // ### implementation-defined + break; + default: + res = ctx->engine->id_number; + break; + } + *result = Value::fromString(res); +} + +void __qmljs_builtin_typeof_name(ExecutionContext *context, Value *result, String *name) +{ + Value res; + __qmljs_builtin_typeof(context, &res, context->getPropertyNoThrow(name)); + if (result) + *result = res; +} + +void __qmljs_builtin_typeof_member(ExecutionContext *context, Value *result, const Value &base, String *name) +{ + Object *obj = base.toObject(context); + Value res; + __qmljs_builtin_typeof(context, &res, obj->get(name)); + if (result) + *result = res; +} + +void __qmljs_builtin_typeof_element(ExecutionContext *context, Value *result, const Value &base, const Value &index) +{ + String *name = index.toString(context); + Object *obj = base.toObject(context); + Value res; + __qmljs_builtin_typeof(context, &res, obj->get(name)); + if (result) + *result = res; +} + +void __qmljs_builtin_post_increment(Value *result, Value *val) +{ + if (val->isInteger() && val->integerValue() < INT_MAX) { + if (result) + *result = *val; + val->int_32 += 1; + return; + } + + double d = __qmljs_to_number(*val); + *val = Value::fromDouble(d + 1); + if (result) + *result = Value::fromDouble(d); +} + +void __qmljs_builtin_post_increment_name(ExecutionContext *context, Value *result, String *name) +{ + Value v = context->getProperty(name); + + if (v.isInteger() && v.integerValue() < INT_MAX) { + if (result) + *result = v; + v.int_32 += 1; + } else { + double d = __qmljs_to_number(v); + if (result) + *result = Value::fromDouble(d); + v = Value::fromDouble(d + 1); + } + + context->setProperty(name, v); +} + +void __qmljs_builtin_post_increment_member(ExecutionContext *context, Value *result, const Value &base, String *name) +{ + Object *o = base.toObject(context); + + Value v = o->get(name); + + if (v.isInteger() && v.integerValue() < INT_MAX) { + if (result) + *result = v; + v.int_32 += 1; + } else { + double d = __qmljs_to_number(v); + if (result) + *result = Value::fromDouble(d); + v = Value::fromDouble(d + 1); + } + + o->put(name, v); +} + +void __qmljs_builtin_post_increment_element(ExecutionContext *context, Value *result, const Value &base, const Value *index) +{ + Object *o = base.toObject(context); + + uint idx = index->asArrayIndex(); + + if (idx == UINT_MAX) { + String *s = index->toString(context); + return __qmljs_builtin_post_increment_member(context, result, base, s); + } + + Value v = o->getIndexed(idx); + + if (v.isInteger() && v.integerValue() < INT_MAX) { + if (result) + *result = v; + v.int_32 += 1; + } else { + double d = __qmljs_to_number(v); + if (result) + *result = Value::fromDouble(d); + v = Value::fromDouble(d + 1); + } + + o->putIndexed(idx, v); +} + +void __qmljs_builtin_post_decrement(Value *result, Value *val) +{ + if (val->isInteger() && val->integerValue() > INT_MIN) { + if (result) + *result = *val; + val->int_32 -= 1; + return; + } + + double d = __qmljs_to_number(*val); + *val = Value::fromDouble(d - 1); + if (result) + *result = Value::fromDouble(d); +} + +void __qmljs_builtin_post_decrement_name(ExecutionContext *context, Value *result, String *name) +{ + Value v = context->getProperty(name); + + if (v.isInteger() && v.integerValue() > INT_MIN) { + if (result) + *result = v; + v.int_32 -= 1; + } else { + double d = __qmljs_to_number(v); + if (result) + *result = Value::fromDouble(d); + v = Value::fromDouble(d - 1); + } + + context->setProperty(name, v); +} + +void __qmljs_builtin_post_decrement_member(ExecutionContext *context, Value *result, const Value &base, String *name) +{ + Object *o = base.toObject(context); + + Value v = o->get(name); + + if (v.isInteger() && v.integerValue() > INT_MIN) { + if (result) + *result = v; + v.int_32 -= 1; + } else { + double d = __qmljs_to_number(v); + if (result) + *result = Value::fromDouble(d); + v = Value::fromDouble(d - 1); + } + + o->put(name, v); +} + +void __qmljs_builtin_post_decrement_element(ExecutionContext *context, Value *result, const Value &base, const Value &index) +{ + Object *o = base.toObject(context); + + uint idx = index.asArrayIndex(); + + if (idx == UINT_MAX) { + String *s = index.toString(context); + return __qmljs_builtin_post_decrement_member(context, result, base, s); + } + + Value v = o->getIndexed(idx); + + if (v.isInteger() && v.integerValue() > INT_MIN) { + if (result) + *result = v; + v.int_32 -= 1; + } else { + double d = __qmljs_to_number(v); + if (result) + *result = Value::fromDouble(d); + v = Value::fromDouble(d - 1); + } + + o->putIndexed(idx, v); +} + +ExecutionContext *__qmljs_builtin_push_with_scope(const Value &o, ExecutionContext *ctx) +{ + Object *obj = o.toObject(ctx); + return ctx->engine->newWithContext(obj); +} + +ExecutionContext *__qmljs_builtin_push_catch_scope(String *exceptionVarName, const Value &exceptionValue, ExecutionContext *ctx) +{ + return ctx->engine->newCatchContext(exceptionVarName, exceptionValue); +} + +ExecutionContext *__qmljs_builtin_pop_scope(ExecutionContext *ctx) +{ + return ctx->engine->popContext(); +} + +void __qmljs_builtin_declare_var(ExecutionContext *ctx, bool deletable, String *name) +{ + ctx->createMutableBinding(name, deletable); +} + +void __qmljs_builtin_define_property(ExecutionContext *ctx, const Value &object, String *name, Value *val) +{ + Object *o = object.asObject(); + assert(o); + + uint idx = name->asArrayIndex(); + Property *pd = (idx != UINT_MAX) ? o->arrayInsert(idx) : o->insertMember(name, Attr_Data); + pd->value = val ? *val : Value::undefinedValue(); +} + +void __qmljs_builtin_define_array(ExecutionContext *ctx, Value *array, Value *values, uint length) +{ + ArrayObject *a = ctx->engine->newArrayObject(); + + // ### FIXME: We need to allocate the array data to avoid crashes other places + // This should rather be done when required + a->arrayReserve(length); + if (length) { + a->arrayDataLen = length; + Property *pd = a->arrayData; + for (uint i = 0; i < length; ++i) { + if (values[i].isEmpty()) { + a->ensureArrayAttributes(); + pd->value = Value::undefinedValue(); + a->arrayAttributes[i].clear(); + } else { + pd->value = values[i]; + } + ++pd; + } + a->setArrayLengthUnchecked(length); + } + *array = Value::fromObject(a); +} + +void __qmljs_builtin_define_getter_setter(ExecutionContext *ctx, const Value &object, String *name, const Value *getter, const Value *setter) +{ + Object *o = object.asObject(); + assert(o); + + uint idx = name->asArrayIndex(); + Property *pd = (idx != UINT_MAX) ? o->arrayInsert(idx, Attr_Accessor) : o->insertMember(name, Attr_Accessor); + pd->setGetter(getter ? getter->asFunctionObject() : 0); + pd->setSetter(setter ? setter->asFunctionObject() : 0); +} + +void __qmljs_builtin_define_object_literal(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value *args, QV4::InternalClass *klass) +{ + Object *o = ctx->engine->newObject(klass); + + for (int i = 0; i < klass->size; ++i) { + if (klass->propertyData[i].isData()) + o->memberData[i].value = *args++; + else { + o->memberData[i].setGetter(args->asFunctionObject()); + args++; + o->memberData[i].setSetter(args->asFunctionObject()); + args++; + } + } + + *result = Value::fromObject(o); +} + +void __qmljs_increment(Value *result, const Value &value) +{ + TRACE1(value); + + if (value.isInteger()) + *result = Value::fromInt32(value.integerValue() + 1); + else { + double d = __qmljs_to_number(value); + *result = Value::fromDouble(d + 1); + } +} + +void __qmljs_decrement(Value *result, const Value &value) +{ + TRACE1(value); + + if (value.isInteger()) + *result = Value::fromInt32(value.integerValue() - 1); + else { + double d = __qmljs_to_number(value); + *result = Value::fromDouble(d - 1); + } +} + +} // namespace QV4 + +QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4runtime_p.h b/src/qml/jsruntime/qv4runtime_p.h new file mode 100644 index 0000000000..836aedf298 --- /dev/null +++ b/src/qml/jsruntime/qv4runtime_p.h @@ -0,0 +1,722 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QMLJS_RUNTIME_H +#define QMLJS_RUNTIME_H + +#include "qv4global_p.h" +#include "qv4value_p.h" +#include "qv4math_p.h" + + +#include +#include +#include +#include + +#include +#include +#include + +//#include + +#ifdef DO_TRACE_INSTR +# define TRACE1(x) fprintf(stderr, " %s\n", __FUNCTION__); +# define TRACE2(x, y) fprintf(stderr, " %s\n", __FUNCTION__); +#else +# define TRACE1(x) +# define TRACE2(x, y) +#endif // TRACE1 + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +enum TypeHint { + PREFERREDTYPE_HINT, + NUMBER_HINT, + STRING_HINT +}; + +struct Function; +struct Object; +struct String; +struct ExecutionContext; +struct FunctionObject; +struct BooleanObject; +struct NumberObject; +struct StringObject; +struct DateObject; +struct RegExpObject; +struct ArrayObject; +struct ErrorObject; +struct ExecutionEngine; +struct InternalClass; + +// context +void __qmljs_call_activation_property(QV4::ExecutionContext *, QV4::Value *result, QV4::String *name, QV4::Value *args, int argc); +void __qmljs_call_property(QV4::ExecutionContext *context, QV4::Value *result, const QV4::Value &that, QV4::String *name, QV4::Value *args, int argc); +void __qmljs_call_property_lookup(QV4::ExecutionContext *context, QV4::Value *result, const QV4::Value &thisObject, uint index, QV4::Value *args, int argc); +void __qmljs_call_element(QV4::ExecutionContext *context, QV4::Value *result, const QV4::Value &that, const QV4::Value &index, QV4::Value *args, int argc); +void __qmljs_call_value(QV4::ExecutionContext *context, QV4::Value *result, const QV4::Value *thisObject, const QV4::Value &func, QV4::Value *args, int argc); + +void __qmljs_construct_activation_property(QV4::ExecutionContext *, QV4::Value *result, QV4::String *name, QV4::Value *args, int argc); +void __qmljs_construct_property(QV4::ExecutionContext *context, QV4::Value *result, const QV4::Value &base, QV4::String *name, QV4::Value *args, int argc); +void __qmljs_construct_value(QV4::ExecutionContext *context, QV4::Value *result, const QV4::Value &func, QV4::Value *args, int argc); + +void __qmljs_builtin_typeof(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value &val); +void __qmljs_builtin_typeof_name(QV4::ExecutionContext *context, QV4::Value* result, QV4::String *name); +void __qmljs_builtin_typeof_member(QV4::ExecutionContext* context, QV4::Value* result, const QV4::Value &base, QV4::String *name); +void __qmljs_builtin_typeof_element(QV4::ExecutionContext* context, QV4::Value *result, const QV4::Value &base, const QV4::Value &index); + +void __qmljs_builtin_post_increment(QV4::Value *result, QV4::Value *val); +void __qmljs_builtin_post_increment_name(QV4::ExecutionContext *context, QV4::Value *result, QV4::String *name); +void __qmljs_builtin_post_increment_member(QV4::ExecutionContext *context, QV4::Value *result, const QV4::Value &base, QV4::String *name); +void __qmljs_builtin_post_increment_element(QV4::ExecutionContext *context, QV4::Value *result, const QV4::Value &base, const QV4::Value *index); + +void __qmljs_builtin_post_decrement(QV4::Value *result, QV4::Value *val); +void __qmljs_builtin_post_decrement_name(QV4::ExecutionContext *context, QV4::Value *result, QV4::String *name); +void __qmljs_builtin_post_decrement_member(QV4::ExecutionContext *context, QV4::Value *result, const QV4::Value &base, QV4::String *name); +void __qmljs_builtin_post_decrement_element(QV4::ExecutionContext *context, QV4::Value *result, const QV4::Value &base, const QV4::Value &index); + +void Q_NORETURN __qmljs_builtin_rethrow(QV4::ExecutionContext *context); +QV4::ExecutionContext *__qmljs_builtin_push_with_scope(const QV4::Value &o, QV4::ExecutionContext *ctx); +QV4::ExecutionContext *__qmljs_builtin_push_catch_scope(QV4::String *exceptionVarName, const QV4::Value &exceptionValue, QV4::ExecutionContext *ctx); +QV4::ExecutionContext *__qmljs_builtin_pop_scope(QV4::ExecutionContext *ctx); +void __qmljs_builtin_declare_var(QV4::ExecutionContext *ctx, bool deletable, QV4::String *name); +void __qmljs_builtin_define_property(QV4::ExecutionContext *ctx, const QV4::Value &object, QV4::String *name, QV4::Value *val); +void __qmljs_builtin_define_array(QV4::ExecutionContext *ctx, QV4::Value *array, QV4::Value *values, uint length); +void __qmljs_builtin_define_getter_setter(QV4::ExecutionContext *ctx, const QV4::Value &object, QV4::String *name, const QV4::Value *getter, const QV4::Value *setter); +void __qmljs_builtin_define_object_literal(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value *args, QV4::InternalClass *klass); + +// constructors +void __qmljs_init_closure(QV4::ExecutionContext *ctx, QV4::Value *result, QV4::Function *clos); +QV4::Function *__qmljs_register_function(QV4::ExecutionContext *ctx, QV4::String *name, + bool hasDirectEval, + bool usesArgumentsObject, bool isStrict, + bool hasNestedFunctions, + QV4::String **formals, unsigned formalCount, + QV4::String **locals, unsigned localCount); + +// strings +Q_QML_EXPORT double __qmljs_string_to_number(const QString &s); +QV4::Value __qmljs_string_from_number(QV4::ExecutionContext *ctx, double number); +QV4::String *__qmljs_string_concat(QV4::ExecutionContext *ctx, QV4::String *first, QV4::String *second); + +// objects +Q_QML_EXPORT QV4::Value __qmljs_object_default_value(QV4::Object *object, int typeHint); +void __qmljs_set_activation_property(QV4::ExecutionContext *ctx, QV4::String *name, const QV4::Value& value); +void __qmljs_set_property(QV4::ExecutionContext *ctx, const QV4::Value &object, QV4::String *name, const QV4::Value &value); +void __qmljs_get_property(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value &object, QV4::String *name); +void __qmljs_get_activation_property(QV4::ExecutionContext *ctx, QV4::Value *result, QV4::String *name); + +void __qmljs_call_global_lookup(QV4::ExecutionContext *context, QV4::Value *result, uint index, QV4::Value *args, int argc); +void __qmljs_construct_global_lookup(QV4::ExecutionContext *context, QV4::Value *result, uint index, QV4::Value *args, int argc); + + +void __qmljs_get_element(QV4::ExecutionContext *ctx, QV4::Value *retval, const QV4::Value &object, const QV4::Value &index); +void __qmljs_set_element(QV4::ExecutionContext *ctx, const QV4::Value &object, const QV4::Value &index, const QV4::Value &value); + +// For each +void __qmljs_foreach_iterator_object(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value &in); +void __qmljs_foreach_next_property_name(QV4::Value *result, const QV4::Value &foreach_iterator); + +// type conversion and testing +QV4::Value __qmljs_to_primitive(const QV4::Value &value, int typeHint); +QV4::Bool __qmljs_to_boolean(const QV4::Value &value); +double __qmljs_to_number(const QV4::Value &value); +QV4::Value __qmljs_to_string(const QV4::Value &value, QV4::ExecutionContext *ctx); +Q_QML_EXPORT QV4::String *__qmljs_convert_to_string(QV4::ExecutionContext *ctx, const QV4::Value &value); +void __qmljs_numberToString(QString *result, double num, int radix = 10); +QV4::Value __qmljs_to_object(QV4::ExecutionContext *ctx, const QV4::Value &value); +QV4::Object *__qmljs_convert_to_object(QV4::ExecutionContext *ctx, const QV4::Value &value); + +QV4::Bool __qmljs_equal_helper(const Value &x, const Value &y); +Q_QML_EXPORT QV4::Bool __qmljs_strict_equal(const QV4::Value &x, const QV4::Value &y); + +// unary operators +typedef void (*UnaryOpName)(QV4::Value *, const QV4::Value &); +void __qmljs_uplus(QV4::Value *result, const QV4::Value &value); +void __qmljs_uminus(QV4::Value *result, const QV4::Value &value); +void __qmljs_compl(QV4::Value *result, const QV4::Value &value); +void __qmljs_not(QV4::Value *result, const QV4::Value &value); +void __qmljs_increment(QV4::Value *result, const QV4::Value &value); +void __qmljs_decrement(QV4::Value *result, const QV4::Value &value); + +void __qmljs_delete_subscript(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value &base, const QV4::Value &index); +void __qmljs_delete_member(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value &base, QV4::String *name); +void __qmljs_delete_name(QV4::ExecutionContext *ctx, QV4::Value *result, QV4::String *name); + +void Q_NORETURN __qmljs_throw(QV4::ExecutionContext*, const QV4::Value &value); + +// binary operators +typedef void (*BinOp)(QV4::Value *result, const QV4::Value &left, const QV4::Value &right); +typedef void (*BinOpContext)(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value &left, const QV4::Value &right); + +void __qmljs_instanceof(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value &left, const QV4::Value &right); +void __qmljs_in(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value &left, const QV4::Value &right); +void __qmljs_add(ExecutionContext *ctx, QV4::Value *result, const QV4::Value &left, const QV4::Value &right); +void __qmljs_bit_or(QV4::Value *result, const QV4::Value &left, const QV4::Value &right); +void __qmljs_bit_xor(QV4::Value *result, const QV4::Value &left, const QV4::Value &right); +void __qmljs_bit_and(QV4::Value *result, const QV4::Value &left, const QV4::Value &right); +void __qmljs_sub(QV4::Value *result, const QV4::Value &left, const QV4::Value &right); +void __qmljs_mul(QV4::Value *result, const QV4::Value &left, const QV4::Value &right); +void __qmljs_div(QV4::Value *result, const QV4::Value &left, const QV4::Value &right); +void __qmljs_mod(QV4::Value *result, const QV4::Value &left, const QV4::Value &right); +void __qmljs_shl(QV4::Value *result, const QV4::Value &left, const QV4::Value &right); +void __qmljs_shr(QV4::Value *result, const QV4::Value &left, const QV4::Value &right); +void __qmljs_ushr(QV4::Value *result, const QV4::Value &left, const QV4::Value &right); +void __qmljs_gt(QV4::Value *result, const QV4::Value &left, const QV4::Value &right); +void __qmljs_lt(QV4::Value *result, const QV4::Value &left, const QV4::Value &right); +void __qmljs_ge(QV4::Value *result, const QV4::Value &left, const QV4::Value &right); +void __qmljs_le(QV4::Value *result, const QV4::Value &left, const QV4::Value &right); +void __qmljs_eq(QV4::Value *result, const QV4::Value &left, const QV4::Value &right); +void __qmljs_ne(QV4::Value *result, const QV4::Value &left, const QV4::Value &right); +void __qmljs_se(QV4::Value *result, const QV4::Value &left, const QV4::Value &right); +void __qmljs_sne(QV4::Value *result, const QV4::Value &left, const QV4::Value &right); + +void __qmljs_add_helper(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value &left, const QV4::Value &right); + + +typedef void (*InplaceBinOpName)(QV4::ExecutionContext *ctx, QV4::String *name, const QV4::Value &value); +void __qmljs_inplace_bit_and_name(QV4::ExecutionContext *ctx, QV4::String *name, const QV4::Value &value); +void __qmljs_inplace_bit_or_name(QV4::ExecutionContext *ctx, QV4::String *name, const QV4::Value &value); +void __qmljs_inplace_bit_xor_name(QV4::ExecutionContext *ctx, QV4::String *name, const QV4::Value &value); +void __qmljs_inplace_add_name(QV4::ExecutionContext *ctx, QV4::String *name, const QV4::Value &value); +void __qmljs_inplace_sub_name(QV4::ExecutionContext *ctx, QV4::String *name, const QV4::Value &value); +void __qmljs_inplace_mul_name(QV4::ExecutionContext *ctx, QV4::String *name, const QV4::Value &value); +void __qmljs_inplace_div_name(QV4::ExecutionContext *ctx, QV4::String *name, const QV4::Value &value); +void __qmljs_inplace_mod_name(QV4::ExecutionContext *ctx, QV4::String *name, const QV4::Value &value); +void __qmljs_inplace_shl_name(QV4::ExecutionContext *ctx, QV4::String *name, const QV4::Value &value); +void __qmljs_inplace_shr_name(QV4::ExecutionContext *ctx, QV4::String *name, const QV4::Value &value); +void __qmljs_inplace_ushr_name(QV4::ExecutionContext *ctx, QV4::String *name, const QV4::Value &value); + +typedef void (*InplaceBinOpElement)(QV4::ExecutionContext *ctx, const QV4::Value &base, const QV4::Value &index, const QV4::Value &rhs); +void __qmljs_inplace_bit_and_element(QV4::ExecutionContext *ctx, const QV4::Value &base, const QV4::Value &index, const QV4::Value &rhs); +void __qmljs_inplace_bit_or_element(QV4::ExecutionContext *ctx, const QV4::Value &base, const QV4::Value &index, const QV4::Value &rhs); +void __qmljs_inplace_bit_xor_element(QV4::ExecutionContext *ctx, const QV4::Value &base, const QV4::Value &index, const QV4::Value &rhs); +void __qmljs_inplace_add_element(QV4::ExecutionContext *ctx, const QV4::Value &base, const QV4::Value &index, const QV4::Value &rhs); +void __qmljs_inplace_sub_element(QV4::ExecutionContext *ctx, const QV4::Value &base, const QV4::Value &index, const QV4::Value &rhs); +void __qmljs_inplace_mul_element(QV4::ExecutionContext *ctx, const QV4::Value &base, const QV4::Value &index, const QV4::Value &rhs); +void __qmljs_inplace_div_element(QV4::ExecutionContext *ctx, const QV4::Value &base, const QV4::Value &index, const QV4::Value &rhs); +void __qmljs_inplace_mod_element(QV4::ExecutionContext *ctx, const QV4::Value &base, const QV4::Value &index, const QV4::Value &rhs); +void __qmljs_inplace_shl_element(QV4::ExecutionContext *ctx, const QV4::Value &base, const QV4::Value &index, const QV4::Value &rhs); +void __qmljs_inplace_shr_element(QV4::ExecutionContext *ctx, const QV4::Value &base, const QV4::Value &index, const QV4::Value &rhs); +void __qmljs_inplace_ushr_element(QV4::ExecutionContext *ctx, const QV4::Value &base, const QV4::Value &index, const QV4::Value &rhs); + +typedef void (*InplaceBinOpMember)(QV4::ExecutionContext *ctx, const QV4::Value &base, QV4::String *name, const QV4::Value &rhs); +void __qmljs_inplace_bit_and_member(QV4::ExecutionContext *ctx, const QV4::Value &base, QV4::String *name, const QV4::Value &rhs); +void __qmljs_inplace_bit_or_member(QV4::ExecutionContext *ctx, const QV4::Value &base, QV4::String *name, const QV4::Value &rhs); +void __qmljs_inplace_bit_xor_member(QV4::ExecutionContext *ctx, const QV4::Value &base, QV4::String *name, const QV4::Value &rhs); +void __qmljs_inplace_add_member(QV4::ExecutionContext *ctx, const QV4::Value &base, QV4::String *name, const QV4::Value &rhs); +void __qmljs_inplace_sub_member(QV4::ExecutionContext *ctx, const QV4::Value &base, QV4::String *name, const QV4::Value &rhs); +void __qmljs_inplace_mul_member(QV4::ExecutionContext *ctx, const QV4::Value &base, QV4::String *name, const QV4::Value &rhs); +void __qmljs_inplace_div_member(QV4::ExecutionContext *ctx, const QV4::Value &base, QV4::String *name, const QV4::Value &rhs); +void __qmljs_inplace_mod_member(QV4::ExecutionContext *ctx, const QV4::Value &base, QV4::String *name, const QV4::Value &rhs); +void __qmljs_inplace_shl_member(QV4::ExecutionContext *ctx, const QV4::Value &base, QV4::String *name, const QV4::Value &rhs); +void __qmljs_inplace_shr_member(QV4::ExecutionContext *ctx, const QV4::Value &base, QV4::String *name, const QV4::Value &rhs); +void __qmljs_inplace_ushr_member(QV4::ExecutionContext *ctx, const QV4::Value &base, QV4::String *name, const QV4::Value &rhs); + +typedef QV4::Bool (*CmpOp)(const QV4::Value &left, const QV4::Value &right); +QV4::Bool __qmljs_cmp_gt(const QV4::Value &left, const QV4::Value &right); +QV4::Bool __qmljs_cmp_lt(const QV4::Value &left, const QV4::Value &right); +QV4::Bool __qmljs_cmp_ge(const QV4::Value &left, const QV4::Value &right); +QV4::Bool __qmljs_cmp_le(const QV4::Value &left, const QV4::Value &right); +QV4::Bool __qmljs_cmp_eq(const QV4::Value &left, const QV4::Value &right); +QV4::Bool __qmljs_cmp_ne(const QV4::Value &left, const QV4::Value &right); +QV4::Bool __qmljs_cmp_se(const QV4::Value &left, const QV4::Value &right); +QV4::Bool __qmljs_cmp_sne(const QV4::Value &left, const QV4::Value &right); + +typedef QV4::Bool (*CmpOpContext)(QV4::ExecutionContext *ctx, const QV4::Value &left, const QV4::Value &right); +QV4::Bool __qmljs_cmp_instanceof(QV4::ExecutionContext *ctx, const QV4::Value &left, const QV4::Value &right); +QV4::Bool __qmljs_cmp_in(QV4::ExecutionContext *ctx, const QV4::Value &left, const QV4::Value &right); + +// type conversion and testing +inline QV4::Value __qmljs_to_primitive(const QV4::Value &value, int typeHint) +{ + QV4::Object *o = value.asObject(); + if (!o) + return value; + return __qmljs_object_default_value(o, typeHint); +} + +inline double __qmljs_to_number(const QV4::Value &value) +{ + switch (value.type()) { + case QV4::Value::Undefined_Type: + return std::numeric_limits::quiet_NaN(); + case QV4::Value::Null_Type: + return 0; + case QV4::Value::Boolean_Type: + return (value.booleanValue() ? 1. : 0.); + case QV4::Value::Integer_Type: + return value.int_32; + case QV4::Value::String_Type: + return __qmljs_string_to_number(value.stringValue()->toQString()); + case QV4::Value::Object_Type: { + QV4::Value prim = __qmljs_to_primitive(value, QV4::NUMBER_HINT); + return __qmljs_to_number(prim); + } + default: // double + return value.doubleValue(); + } +} + +inline QV4::Value __qmljs_to_string(const QV4::Value &value, QV4::ExecutionContext *ctx) +{ + if (value.isString()) + return value; + return QV4::Value::fromString(__qmljs_convert_to_string(ctx, value)); +} + +inline QV4::Value __qmljs_to_object(QV4::ExecutionContext *ctx, const QV4::Value &value) +{ + if (value.isObject()) + return value; + return QV4::Value::fromObject(__qmljs_convert_to_object(ctx, value)); +} + + +inline void __qmljs_uplus(QV4::Value *result, const QV4::Value &value) +{ + TRACE1(value); + + *result = value; + if (result->tryIntegerConversion()) + return; + + double n = __qmljs_to_number(value); + *result = QV4::Value::fromDouble(n); +} + +inline void __qmljs_uminus(QV4::Value *result, const QV4::Value &value) +{ + TRACE1(value); + + // +0 != -0, so we need to convert to double when negating 0 + if (value.isInteger() && value.integerValue()) + *result = QV4::Value::fromInt32(-value.integerValue()); + else { + double n = __qmljs_to_number(value); + *result = QV4::Value::fromDouble(-n); + } +} + +inline void __qmljs_compl(QV4::Value *result, const QV4::Value &value) +{ + TRACE1(value); + + int n; + if (value.isConvertibleToInt()) + n = value.int_32; + else + n = QV4::Value::toInt32(__qmljs_to_number(value)); + + *result = QV4::Value::fromInt32(~n); +} + +inline void __qmljs_not(QV4::Value *result, const QV4::Value &value) +{ + TRACE1(value); + + bool b = value.toBoolean(); + *result = QV4::Value::fromBoolean(!b); +} + +// binary operators +inline void __qmljs_bit_or(QV4::Value *result, const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + + if (QV4::Value::integerCompatible(left, right)) { + *result = QV4::Value::fromInt32(left.integerValue() | right.integerValue()); + return; + } + + int lval = QV4::Value::toInt32(__qmljs_to_number(left)); + int rval = QV4::Value::toInt32(__qmljs_to_number(right)); + *result = QV4::Value::fromInt32(lval | rval); +} + +inline void __qmljs_bit_xor(QV4::Value *result, const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + + if (QV4::Value::integerCompatible(left, right)) { + *result = QV4::Value::fromInt32(left.integerValue() ^ right.integerValue()); + return; + } + + int lval = QV4::Value::toInt32(__qmljs_to_number(left)); + int rval = QV4::Value::toInt32(__qmljs_to_number(right)); + *result = QV4::Value::fromInt32(lval ^ rval); +} + +inline void __qmljs_bit_and(QV4::Value *result, const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + + if (QV4::Value::integerCompatible(left, right)) { + *result = QV4::Value::fromInt32(left.integerValue() & right.integerValue()); + return; + } + + int lval = QV4::Value::toInt32(__qmljs_to_number(left)); + int rval = QV4::Value::toInt32(__qmljs_to_number(right)); + *result = QV4::Value::fromInt32(lval & rval); +} + +inline void __qmljs_add(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + + if (QV4::Value::integerCompatible(left, right)) { + *result = add_int32(left.integerValue(), right.integerValue()); + return; + } + + if (QV4::Value::bothDouble(left, right)) { + *result = QV4::Value::fromDouble(left.doubleValue() + right.doubleValue()); + return; + } + + __qmljs_add_helper(ctx, result, left, right); +} + +inline void __qmljs_sub(QV4::Value *result, const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + + if (QV4::Value::integerCompatible(left, right)) { + *result = sub_int32(left.integerValue(), right.integerValue()); + return; + } + + double lval = __qmljs_to_number(left); + double rval = __qmljs_to_number(right); + *result = QV4::Value::fromDouble(lval - rval); +} + +inline void __qmljs_mul(QV4::Value *result, const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + + if (QV4::Value::integerCompatible(left, right)) { + *result = mul_int32(left.integerValue(), right.integerValue()); + return; + } + + double lval = __qmljs_to_number(left); + double rval = __qmljs_to_number(right); + *result = QV4::Value::fromDouble(lval * rval); +} + +inline void __qmljs_div(QV4::Value *result, const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + + double lval = __qmljs_to_number(left); + double rval = __qmljs_to_number(right); + *result = QV4::Value::fromDouble(lval / rval); +} + +inline void __qmljs_mod(QV4::Value *result, const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + + if (QV4::Value::integerCompatible(left, right) && right.integerValue() != 0) { + int intRes = left.integerValue() % right.integerValue(); + if (intRes != 0 || left.integerValue() >= 0) { + *result = QV4::Value::fromInt32(intRes); + return; + } + } + + double lval = __qmljs_to_number(left); + double rval = __qmljs_to_number(right); + *result = QV4::Value::fromDouble(std::fmod(lval, rval)); +} + +inline void __qmljs_shl(QV4::Value *result, const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + + if (QV4::Value::integerCompatible(left, right)) { + *result = QV4::Value::fromInt32(left.integerValue() << ((uint(right.integerValue()) & 0x1f))); + return; + } + + int lval = QV4::Value::toInt32(__qmljs_to_number(left)); + unsigned rval = QV4::Value::toUInt32(__qmljs_to_number(right)) & 0x1f; + *result = QV4::Value::fromInt32(lval << rval); +} + +inline void __qmljs_shr(QV4::Value *result, const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + + if (QV4::Value::integerCompatible(left, right)) { + *result = QV4::Value::fromInt32(left.integerValue() >> ((uint(right.integerValue()) & 0x1f))); + return; + } + + int lval = QV4::Value::toInt32(__qmljs_to_number(left)); + unsigned rval = QV4::Value::toUInt32(__qmljs_to_number(right)) & 0x1f; + *result = QV4::Value::fromInt32(lval >> rval); +} + +inline void __qmljs_ushr(QV4::Value *result, const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + + uint res; + if (QV4::Value::integerCompatible(left, right)) { + res = uint(left.integerValue()) >> (uint(right.integerValue()) & 0x1f); + } else { + unsigned lval = QV4::Value::toUInt32(__qmljs_to_number(left)); + unsigned rval = QV4::Value::toUInt32(__qmljs_to_number(right)) & 0x1f; + res = lval >> rval; + } + + if (res > INT_MAX) + *result = QV4::Value::fromDouble(res); + else + *result = QV4::Value::fromInt32(res); +} + +inline void __qmljs_gt(QV4::Value *result, const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + + *result = QV4::Value::fromBoolean(__qmljs_cmp_gt(left, right)); +} + +inline void __qmljs_lt(QV4::Value *result, const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + + *result = QV4::Value::fromBoolean(__qmljs_cmp_lt(left, right)); +} + +inline void __qmljs_ge(QV4::Value *result, const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + + *result = QV4::Value::fromBoolean(__qmljs_cmp_ge(left, right)); +} + +inline void __qmljs_le(QV4::Value *result, const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + + *result = QV4::Value::fromBoolean(__qmljs_cmp_le(left, right)); +} + +inline void __qmljs_eq(QV4::Value *result, const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + + *result = QV4::Value::fromBoolean(__qmljs_cmp_eq(left, right)); +} + +inline void __qmljs_ne(QV4::Value *result, const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + + *result = QV4::Value::fromBoolean(!__qmljs_cmp_eq(left, right)); +} + +inline void __qmljs_se(QV4::Value *result, const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + + bool r = __qmljs_strict_equal(left, right); + *result = QV4::Value::fromBoolean(r); +} + +inline void __qmljs_sne(QV4::Value *result, const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + + bool r = ! __qmljs_strict_equal(left, right); + *result = QV4::Value::fromBoolean(r); +} + +inline QV4::Bool __qmljs_cmp_gt(const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + if (QV4::Value::integerCompatible(left, right)) + return left.integerValue() > right.integerValue(); + + QV4::Value l = __qmljs_to_primitive(left, QV4::NUMBER_HINT); + QV4::Value r = __qmljs_to_primitive(right, QV4::NUMBER_HINT); + + if (QV4::Value::bothDouble(l, r)) { + return l.doubleValue() > r.doubleValue(); + } else if (l.isString() && r.isString()) { + return r.stringValue()->compare(l.stringValue()); + } else { + double dl = __qmljs_to_number(l); + double dr = __qmljs_to_number(r); + return dl > dr; + } +} + +inline QV4::Bool __qmljs_cmp_lt(const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + if (QV4::Value::integerCompatible(left, right)) + return left.integerValue() < right.integerValue(); + + QV4::Value l = __qmljs_to_primitive(left, QV4::NUMBER_HINT); + QV4::Value r = __qmljs_to_primitive(right, QV4::NUMBER_HINT); + + if (QV4::Value::bothDouble(l, r)) { + return l.doubleValue() < r.doubleValue(); + } else if (l.isString() && r.isString()) { + return l.stringValue()->compare(r.stringValue()); + } else { + double dl = __qmljs_to_number(l); + double dr = __qmljs_to_number(r); + return dl < dr; + } +} + +inline QV4::Bool __qmljs_cmp_ge(const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + if (QV4::Value::integerCompatible(left, right)) + return left.integerValue() >= right.integerValue(); + + QV4::Value l = __qmljs_to_primitive(left, QV4::NUMBER_HINT); + QV4::Value r = __qmljs_to_primitive(right, QV4::NUMBER_HINT); + + if (QV4::Value::bothDouble(l, r)) { + return l.doubleValue() >= r.doubleValue(); + } else if (l.isString() && r.isString()) { + return !l.stringValue()->compare(r.stringValue()); + } else { + double dl = __qmljs_to_number(l); + double dr = __qmljs_to_number(r); + return dl >= dr; + } +} + +inline QV4::Bool __qmljs_cmp_le(const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + if (QV4::Value::integerCompatible(left, right)) + return left.integerValue() <= right.integerValue(); + + QV4::Value l = __qmljs_to_primitive(left, QV4::NUMBER_HINT); + QV4::Value r = __qmljs_to_primitive(right, QV4::NUMBER_HINT); + + if (QV4::Value::bothDouble(l, r)) { + return l.doubleValue() <= r.doubleValue(); + } else if (l.isString() && r.isString()) { + return !r.stringValue()->compare(l.stringValue()); + } else { + double dl = __qmljs_to_number(l); + double dr = __qmljs_to_number(r); + return dl <= dr; + } +} + +inline QV4::Bool __qmljs_cmp_eq(const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + + if (left.val == right.val) + // NaN != NaN + return (left.tag & QV4::Value::NotDouble_Mask) != QV4::Value::NaN_Mask; + + if (left.type() == right.type()) { + if (left.isManaged()) + return left.managed()->isEqualTo(right.managed()); + return false; + } + + return __qmljs_equal_helper(left, right); +} + +inline QV4::Bool __qmljs_cmp_ne(const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + + return !__qmljs_cmp_eq(left, right); +} + +inline QV4::Bool __qmljs_cmp_se(const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + + return __qmljs_strict_equal(left, right); +} + +inline QV4::Bool __qmljs_cmp_sne(const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + + return ! __qmljs_strict_equal(left, right); +} + +inline QV4::Bool __qmljs_cmp_instanceof(QV4::ExecutionContext *ctx, const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + + QV4::Value v; + __qmljs_instanceof(ctx, &v, left, right); + return v.booleanValue(); +} + +inline uint __qmljs_cmp_in(QV4::ExecutionContext *ctx, const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + + QV4::Value v; + __qmljs_in(ctx, &v, left, right); + return v.booleanValue(); +} + +} // namespace QV4 + +QT_END_NAMESPACE + +#endif // QMLJS_RUNTIME_H diff --git a/src/qml/jsruntime/qv4script.cpp b/src/qml/jsruntime/qv4script.cpp new file mode 100644 index 0000000000..3de218a451 --- /dev/null +++ b/src/qml/jsruntime/qv4script.cpp @@ -0,0 +1,249 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4script_p.h" +#include "qv4mm_p.h" +#include "qv4functionobject_p.h" +#include "qv4function_p.h" +#include "qv4context_p.h" +#include "qv4debugging_p.h" +#include "qv4exception_p.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace QV4; + +struct QmlBindingWrapper : FunctionObject +{ + QmlBindingWrapper(ExecutionContext *scope, Function *f, Object *qml) + : FunctionObject(scope, scope->engine->id_eval) + , qml(qml) + { + vtbl = &static_vtbl; + function = f; + usesArgumentsObject = function->usesArgumentsObject; + needsActivation = function->needsActivation(); + defineReadonlyProperty(scope->engine->id_length, Value::fromInt32(1)); + + qmlContext = scope->engine->newQmlContext(this, qml); + scope->engine->popContext(); + } + + static Value call(Managed *that, const Value &, Value *, int); + static void markObjects(Managed *m) + { + QmlBindingWrapper *wrapper = static_cast(m); + if (wrapper->qml) + wrapper->qml->mark(); + FunctionObject::markObjects(m); + wrapper->qmlContext->mark(); + } + +protected: + static const ManagedVTable static_vtbl; + +private: + Object *qml; + CallContext *qmlContext; + +}; + +DEFINE_MANAGED_VTABLE(QmlBindingWrapper); + +Value QmlBindingWrapper::call(Managed *that, const Value &, Value *, int) +{ + ExecutionEngine *engine = that->engine(); + QmlBindingWrapper *This = static_cast(that); + + CallContext *ctx = This->qmlContext; + std::fill(ctx->locals, ctx->locals + ctx->function->varCount, Value::undefinedValue()); + engine->pushContext(ctx); + Value result = This->function->code(ctx, This->function->codeData); + engine->popContext(); + + return result; + +} + + +void Script::parse() +{ + if (parsed) + return; + + using namespace QQmlJS; + + parsed = true; + + ExecutionEngine *v4 = scope->engine; + + MemoryManager::GCBlocker gcBlocker(v4->memoryManager); + + V4IR::Module module; + + QQmlJS::Engine ee, *engine = ⅇ + Lexer lexer(engine); + lexer.setCode(sourceCode, line, parseAsBinding); + Parser parser(engine); + + const bool parsed = parser.parseProgram(); + + DiagnosticMessage *error = 0, **errIt = &error; + foreach (const QQmlJS::DiagnosticMessage &m, parser.diagnosticMessages()) { + if (m.isError()) { + *errIt = new DiagnosticMessage; + (*errIt)->fileName = sourceFile; + (*errIt)->offset = m.loc.offset; + (*errIt)->length = m.loc.length; + (*errIt)->startLine = m.loc.startLine; + (*errIt)->startColumn = m.loc.startColumn; + (*errIt)->type = DiagnosticMessage::Error; + (*errIt)->message = m.message; + errIt = &(*errIt)->next; + } else { + qWarning() << sourceFile << ':' << m.loc.startLine << ':' << m.loc.startColumn + << ": warning: " << m.message; + } + } + if (error) + scope->throwSyntaxError(error); + + if (parsed) { + using namespace AST; + Program *program = AST::cast(parser.rootNode()); + if (!program) { + // if parsing was successful, and we have no program, then + // we're done...: + return; + } + + QStringList inheritedLocals; + if (inheritContext) + for (String * const *i = scope->variables(), * const *ei = i + scope->variableCount(); i < ei; ++i) + inheritedLocals.append(*i ? (*i)->toQString() : QString()); + + Codegen cg(scope, strictMode); + V4IR::Function *globalIRCode = cg(sourceFile, sourceCode, program, &module, + parseAsBinding ? QQmlJS::Codegen::QmlBinding : QQmlJS::Codegen::EvalCode, inheritedLocals); + QScopedPointer isel(v4->iselFactory->create(v4, &module)); + if (inheritContext) + isel->setUseFastLookups(false); + if (globalIRCode) + vmFunction = isel->vmFunction(globalIRCode); + } + + if (!vmFunction) + // ### FIX file/line number + v4->current->throwError(QV4::Value::fromObject(v4->newSyntaxErrorObject(v4->current, 0))); +} + +Value Script::run() +{ + if (!parsed) + parse(); + if (!vmFunction) + return Value::undefinedValue(); + + QV4::ExecutionEngine *engine = scope->engine; + + if (qml.isEmpty()) { + TemporaryAssignment savedGlobalCode(engine->globalCode, vmFunction); + + bool strict = scope->strictMode; + Lookup *lookups = scope->lookups; + + scope->strictMode = vmFunction->isStrict; + scope->lookups = vmFunction->lookups; + + QV4::Value result; + try { + result = vmFunction->code(scope, vmFunction->codeData); + } catch (Exception &e) { + scope->strictMode = strict; + scope->lookups = lookups; + throw; + } + + return result; + + } else { + FunctionObject *f = new (engine->memoryManager) QmlBindingWrapper(scope, vmFunction, qml.value().asObject()); + return f->call(Value::undefinedValue(), 0, 0); + } +} + +Function *Script::function() +{ + if (!parsed) + parse(); + return vmFunction; +} + +Value Script::qmlBinding() +{ + if (!parsed) + parse(); + QV4::ExecutionEngine *v4 = scope->engine; + return Value::fromObject(new (v4->memoryManager) QmlBindingWrapper(scope, vmFunction, qml.value().asObject())); +} + +QV4::Value Script::evaluate(ExecutionEngine *engine, const QString &script, Object *scopeObject) +{ + QV4::Script qmlScript(engine, scopeObject, script, QString()); + + QV4::ExecutionContext *ctx = engine->current; + QV4::Value result = QV4::Value::undefinedValue(); + try { + qmlScript.parse(); + result = qmlScript.run(); + } catch (QV4::Exception &e) { + e.accept(ctx); + } + return result; +} diff --git a/src/qml/jsruntime/qv4script_p.h b/src/qml/jsruntime/qv4script_p.h new file mode 100644 index 0000000000..274a87db26 --- /dev/null +++ b/src/qml/jsruntime/qv4script_p.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4SCRIPT_H +#define QV4SCRIPT_H + +#include "qv4global_p.h" +#include "qv4engine_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct ExecutionContext; + +struct Q_QML_EXPORT Script { + Script(ExecutionContext *scope, const QString &sourceCode, const QString &source = QString(), int line = 1, int column = 0) + : sourceFile(source), line(line), column(column), sourceCode(sourceCode) + , scope(scope), strictMode(false), inheritContext(false), parsed(false) + , vmFunction(0), parseAsBinding(false) {} + Script(ExecutionEngine *engine, Object *qml, const QString &sourceCode, const QString &source = QString(), int line = 1, int column = 0) + : sourceFile(source), line(line), column(column), sourceCode(sourceCode) + , scope(engine->rootContext), strictMode(false), inheritContext(true), parsed(false) + , qml(Value::fromObject(qml)), vmFunction(0), parseAsBinding(true) {} + QString sourceFile; + int line; + int column; + QString sourceCode; + ExecutionContext *scope; + bool strictMode; + bool inheritContext; + bool parsed; + QV4::PersistentValue qml; + Function *vmFunction; + bool parseAsBinding; + + void parse(); + Value run(); + Value qmlBinding(); + + Function *function(); + + + static Value evaluate(ExecutionEngine *engine, const QString &script, Object *scopeObject); +}; + +} + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/jsruntime/qv4sequenceobject.cpp b/src/qml/jsruntime/qv4sequenceobject.cpp new file mode 100644 index 0000000000..c4d9a71519 --- /dev/null +++ b/src/qml/jsruntime/qv4sequenceobject.cpp @@ -0,0 +1,651 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include "qv4sequenceobject_p.h" + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +using namespace QV4; + +// helper function to generate valid warnings if errors occur during sequence operations. +static void generateWarning(QV4::ExecutionContext *ctx, const QString& description) +{ + QQmlEngine *engine = ctx->engine->v8Engine->engine(); + if (!engine) + return; + QQmlError retn; + retn.setDescription(description); + + QV4::ExecutionEngine::StackFrame frame = ctx->engine->currentStackFrame(); + + retn.setLine(frame.line); + retn.setUrl(QUrl(frame.source)); + QQmlEnginePrivate::warning(engine, retn); +} + +// F(elementType, elementTypeName, sequenceType, defaultValue) +#define FOREACH_QML_SEQUENCE_TYPE(F) \ + F(int, Int, QList, 0) \ + F(qreal, Real, QList, 0.0) \ + F(bool, Bool, QList, false) \ + F(QString, String, QList, QString()) \ + F(QString, QString, QStringList, QString()) \ + F(QUrl, Url, QList, QUrl()) + +static QV4::Value convertElementToValue(QV4::ExecutionEngine *engine, const QString &element) +{ + return QV4::Value::fromString(engine, element); +} + +static QV4::Value convertElementToValue(QV4::ExecutionEngine *, int element) +{ + return QV4::Value::fromInt32(element); +} + +static QV4::Value convertElementToValue(QV4::ExecutionEngine *engine, const QUrl &element) +{ + return QV4::Value::fromString(engine, element.toString()); +} + +static QV4::Value convertElementToValue(QV4::ExecutionEngine *, qreal element) +{ + return QV4::Value::fromDouble(element); +} + +static QV4::Value convertElementToValue(QV4::ExecutionEngine *, bool element) +{ + return QV4::Value::fromBoolean(element); +} + +static QString convertElementToString(const QString &element) +{ + return element; +} + +static QString convertElementToString(int element) +{ + return QString::number(element); +} + +static QString convertElementToString(const QUrl &element) +{ + return element.toString(); +} + +static QString convertElementToString(qreal element) +{ + QString qstr; + __qmljs_numberToString(&qstr, element, 10); + return qstr; +} + +static QString convertElementToString(bool element) +{ + if (element) + return QStringLiteral("true"); + else + return QStringLiteral("false"); +} + +template ElementType convertValueToElement(const QV4::Value &value); + +template <> QString convertValueToElement(const QV4::Value &value) +{ + return value.toQString(); +} + +template <> int convertValueToElement(const QV4::Value &value) +{ + return value.toInt32(); +} + +template <> QUrl convertValueToElement(const QV4::Value &value) +{ + return QUrl(value.toQString()); +} + +template <> qreal convertValueToElement(const QV4::Value &value) +{ + return value.toNumber(); +} + +template <> bool convertValueToElement(const QV4::Value &value) +{ + return value.toBoolean(); +} + +template +class QQmlSequence : public QV4::Object +{ + Q_MANAGED +public: + QQmlSequence(QV4::ExecutionEngine *engine, const Container &container) + : QV4::Object(engine) + , m_container(container) + , m_object(0) + , m_propertyIndex(-1) + , m_isReference(false) + { + type = Type_QmlSequence; + vtbl = &static_vtbl; + prototype = engine->sequencePrototype; + init(engine); + } + + QQmlSequence(QV4::ExecutionEngine *engine, QObject *object, int propertyIndex) + : QV4::Object(engine) + , m_object(object) + , m_propertyIndex(propertyIndex) + , m_isReference(true) + { + type = Type_QmlSequence; + vtbl = &static_vtbl; + prototype = engine->sequencePrototype; + loadReference(); + init(engine); + } + + void init(ExecutionEngine *engine) + { + defineAccessorProperty(engine, QStringLiteral("length"), method_get_length, method_set_length); + } + + QV4::Value containerGetIndexed(uint index, bool *hasProperty) + { + /* Qt containers have int (rather than uint) allowable indexes. */ + if (index > INT_MAX) { + generateWarning(engine()->current, QLatin1String("Index out of range during indexed get")); + if (hasProperty) + *hasProperty = false; + return QV4::Value::undefinedValue(); + } + if (m_isReference) { + if (!m_object) { + if (hasProperty) + *hasProperty = false; + return QV4::Value::undefinedValue(); + } + loadReference(); + } + qint32 signedIdx = static_cast(index); + if (signedIdx < m_container.count()) { + if (hasProperty) + *hasProperty = true; + return convertElementToValue(engine(), m_container.at(signedIdx)); + } + if (hasProperty) + *hasProperty = false; + return QV4::Value::undefinedValue(); + } + + void containerPutIndexed(uint index, const QV4::Value &value) + { + /* Qt containers have int (rather than uint) allowable indexes. */ + if (index > INT_MAX) { + generateWarning(engine()->current, QLatin1String("Index out of range during indexed set")); + return; + } + + if (m_isReference) { + if (!m_object) + return; + loadReference(); + } + + qint32 signedIdx = static_cast(index); + + int count = m_container.count(); + + typename Container::value_type element = convertValueToElement(value); + + if (signedIdx == count) { + m_container.append(element); + } else if (signedIdx < count) { + m_container[signedIdx] = element; + } else { + /* according to ECMA262r3 we need to insert */ + /* the value at the given index, increasing length to index+1. */ + m_container.reserve(signedIdx + 1); + while (signedIdx > count++) { + m_container.append(typename Container::value_type()); + } + m_container.append(element); + } + + if (m_isReference) + storeReference(); + } + + QV4::PropertyAttributes containerQueryIndexed(uint index) const + { + /* Qt containers have int (rather than uint) allowable indexes. */ + if (index > INT_MAX) { + generateWarning(engine()->current, QLatin1String("Index out of range during indexed query")); + return QV4::Attr_Invalid; + } + if (m_isReference) { + if (!m_object) + return QV4::Attr_Invalid; + loadReference(); + } + qint32 signedIdx = static_cast(index); + return (signedIdx < m_container.count()) ? QV4::Attr_Data : QV4::Attr_Invalid; + } + + Property *containerAdvanceIterator(ObjectIterator *it, String **name, uint *index, PropertyAttributes *attrs) + { + *name = 0; + *index = UINT_MAX; + + if (m_isReference) { + if (!m_object) + return QV4::Object::advanceIterator(this, it, name, index, attrs); + loadReference(); + } + + if (it->arrayIndex < m_container.count()) { + if (attrs) + *attrs = QV4::Attr_Data; + *index = it->arrayIndex; + ++it->arrayIndex; + it->tmpDynamicProperty.value = convertElementToValue(engine(), m_container.at(*index)); + return &it->tmpDynamicProperty; + } + return QV4::Object::advanceIterator(this, it, name, index, attrs); + } + + bool containerDeleteIndexedProperty(uint index) + { + /* Qt containers have int (rather than uint) allowable indexes. */ + if (index > INT_MAX) + return false; + if (m_isReference) { + if (!m_object) + return false; + loadReference(); + } + qint32 signedIdx = static_cast(index); + + if (signedIdx >= m_container.count()) + return false; + + /* according to ECMA262r3 it should be Undefined, */ + /* but we cannot, so we insert a default-value instead. */ + m_container.replace(signedIdx, typename Container::value_type()); + + if (m_isReference) + storeReference(); + + return true; + } + + bool containerIsEqualTo(Managed *other) + { + QQmlSequence *otherSequence = other->as >(); + if (!otherSequence) + return false; + if (m_isReference && otherSequence->m_isReference) { + return m_object == otherSequence->m_object && m_propertyIndex == otherSequence->m_propertyIndex; + } else if (!m_isReference && !otherSequence->m_isReference) { + return this == otherSequence; + } + return false; + } + + struct DefaultCompareFunctor + { + bool operator()(typename Container::value_type lhs, typename Container::value_type rhs) + { + return convertElementToString(lhs) < convertElementToString(rhs); + } + }; + + struct CompareFunctor + { + CompareFunctor(QV4::ExecutionContext *ctx, const QV4::Value &compareFn) + : m_ctx(ctx), m_compareFn(compareFn) + {} + + bool operator()(typename Container::value_type lhs, typename Container::value_type rhs) + { + QV4::Managed *fun = this->m_compareFn.asManaged(); + QV4::Value argv[2] = { + convertElementToValue(this->m_ctx->engine, lhs), + convertElementToValue(this->m_ctx->engine, rhs) + }; + QV4::Value result = fun->call(QV4::Value::fromObject(this->m_ctx->engine->globalObject), argv, 2); + return result.toNumber() < 0; + } + + private: + QV4::ExecutionContext *m_ctx; + QV4::Value m_compareFn; + }; + + void sort(QV4::SimpleCallContext *ctx) + { + if (m_isReference) { + if (!m_object) + return; + loadReference(); + } + + if (ctx->argumentCount == 1 && ctx->arguments[0].asFunctionObject()) { + QV4::Value compareFn = ctx->arguments[0]; + CompareFunctor cf(ctx, compareFn); + qSort(m_container.begin(), m_container.end(), cf); + } else { + DefaultCompareFunctor cf; + qSort(m_container.begin(), m_container.end(), cf); + } + + if (m_isReference) + storeReference(); + } + + static QV4::Value method_get_length(QV4::SimpleCallContext *ctx) + { + QQmlSequence *This = ctx->thisObject.as >(); + if (!This) + ctx->throwTypeError(); + + if (This->m_isReference) { + if (!This->m_object) + return QV4::Value::fromInt32(0); + This->loadReference(); + } + return QV4::Value::fromInt32(This->m_container.count()); + } + + static QV4::Value method_set_length(QV4::SimpleCallContext* ctx) + { + QQmlSequence *This = ctx->thisObject.as >(); + if (!This) + ctx->throwTypeError(); + + quint32 newLength = ctx->arguments[0].toUInt32(); + /* Qt containers have int (rather than uint) allowable indexes. */ + if (newLength > INT_MAX) { + generateWarning(ctx, QLatin1String("Index out of range during length set")); + return QV4::Value::undefinedValue(); + } + /* Read the sequence from the QObject property if we're a reference */ + if (This->m_isReference) { + if (!This->m_object) + return QV4::Value::undefinedValue(); + This->loadReference(); + } + /* Determine whether we need to modify the sequence */ + qint32 newCount = static_cast(newLength); + qint32 count = This->m_container.count(); + if (newCount == count) { + return QV4::Value::undefinedValue(); + } else if (newCount > count) { + /* according to ECMA262r3 we need to insert */ + /* undefined values increasing length to newLength. */ + /* We cannot, so we insert default-values instead. */ + This->m_container.reserve(newCount); + while (newCount > count++) { + This->m_container.append(typename Container::value_type()); + } + } else { + /* according to ECMA262r3 we need to remove */ + /* elements until the sequence is the required length. */ + while (newCount < count) { + count--; + This->m_container.removeAt(count); + } + } + /* write back if required. */ + if (This->m_isReference) { + /* write back. already checked that object is non-null, so skip that check here. */ + This->storeReference(); + } + return QV4::Value::undefinedValue(); + } + + QVariant toVariant() const + { return QVariant::fromValue(m_container); } + + static QVariant toVariant(QV4::ArrayObject *array) + { + Container result; + uint32_t length = array->arrayLength(); + for (uint32_t i = 0; i < length; ++i) + result << convertValueToElement(array->getIndexed(i)); + return QVariant::fromValue(result); + } + +private: + void loadReference() const + { + Q_ASSERT(m_object); + Q_ASSERT(m_isReference); + void *a[] = { &m_container, 0 }; + QMetaObject::metacall(m_object, QMetaObject::ReadProperty, m_propertyIndex, a); + } + + void storeReference() + { + Q_ASSERT(m_object); + Q_ASSERT(m_isReference); + int status = -1; + QQmlPropertyPrivate::WriteFlags flags = QQmlPropertyPrivate::DontRemoveBinding; + void *a[] = { &m_container, 0, &status, &flags }; + QMetaObject::metacall(m_object, QMetaObject::WriteProperty, m_propertyIndex, a); + } + + mutable Container m_container; + QPointer m_object; + int m_propertyIndex; + bool m_isReference; + + static QV4::Value getIndexed(QV4::Managed *that, uint index, bool *hasProperty) + { return static_cast *>(that)->containerGetIndexed(index, hasProperty); } + static void putIndexed(Managed *that, uint index, const QV4::Value &value) + { static_cast *>(that)->containerPutIndexed(index, value); } + static QV4::PropertyAttributes queryIndexed(const QV4::Managed *that, uint index) + { return static_cast *>(that)->containerQueryIndexed(index); } + static bool deleteIndexedProperty(QV4::Managed *that, uint index) + { return static_cast *>(that)->containerDeleteIndexedProperty(index); } + static bool isEqualTo(Managed *that, Managed *other) + { return static_cast *>(that)->containerIsEqualTo(other); } + static Property *advanceIterator(Managed *that, ObjectIterator *it, String **name, uint *index, PropertyAttributes *attrs) + { return static_cast *>(that)->containerAdvanceIterator(it, name, index, attrs); } + + static void destroy(Managed *that) + { + static_cast *>(that)->~QQmlSequence(); + } +}; + +typedef QQmlSequence QQmlQStringList; +template<> +DEFINE_MANAGED_VTABLE(QQmlQStringList); +typedef QQmlSequence > QQmlStringList; +template<> +DEFINE_MANAGED_VTABLE(QQmlStringList); +typedef QQmlSequence > QQmlIntList; +template<> +DEFINE_MANAGED_VTABLE(QQmlIntList); +typedef QQmlSequence > QQmlUrlList; +template<> +DEFINE_MANAGED_VTABLE(QQmlUrlList); +typedef QQmlSequence > QQmlBoolList; +template<> +DEFINE_MANAGED_VTABLE(QQmlBoolList); +typedef QQmlSequence > QQmlRealList; +template<> +DEFINE_MANAGED_VTABLE(QQmlRealList); + +#define REGISTER_QML_SEQUENCE_METATYPE(unused, unused2, SequenceType, unused3) qRegisterMetaType(#SequenceType); +SequencePrototype::SequencePrototype(ExecutionEngine *engine) + : QV4::Object(engine) +{ + prototype = engine->arrayPrototype; + FOREACH_QML_SEQUENCE_TYPE(REGISTER_QML_SEQUENCE_METATYPE) +} +#undef REGISTER_QML_SEQUENCE_METATYPE + +void SequencePrototype::init(QV4::ExecutionEngine *engine) +{ + defineDefaultProperty(engine, QStringLiteral("sort"), method_sort, 1); + defineDefaultProperty(engine, QStringLiteral("valueOf"), method_valueOf, 0); +} + +QV4::Value SequencePrototype::method_sort(QV4::SimpleCallContext *ctx) +{ + QV4::Object *o = ctx->thisObject.asObject(); + if (!o || !o->isListType()) + ctx->throwTypeError(); + + if (ctx->argumentCount >= 2) + return ctx->thisObject; + +#define CALL_SORT(SequenceElementType, SequenceElementTypeName, SequenceType, DefaultValue) \ + if (QQml##SequenceElementTypeName##List *s = o->as()) { \ + s->sort(ctx); \ + } else + + FOREACH_QML_SEQUENCE_TYPE(CALL_SORT) + +#undef CALL_SORT + return ctx->thisObject; +} + +#define IS_SEQUENCE(unused1, unused2, SequenceType, unused3) \ + if (sequenceTypeId == qMetaTypeId()) { \ + return true; \ + } else + +bool SequencePrototype::isSequenceType(int sequenceTypeId) +{ + FOREACH_QML_SEQUENCE_TYPE(IS_SEQUENCE) { /* else */ return false; } +} +#undef IS_SEQUENCE + +#define NEW_REFERENCE_SEQUENCE(ElementType, ElementTypeName, SequenceType, unused) \ + if (sequenceType == qMetaTypeId()) { \ + QV4::Object *obj = new (engine->memoryManager) QQml##ElementTypeName##List(engine, object, propertyIndex); \ + return QV4::Value::fromObject(obj); \ + } else + +QV4::Value SequencePrototype::newSequence(QV4::ExecutionEngine *engine, int sequenceType, QObject *object, int propertyIndex, bool *succeeded) +{ + // This function is called when the property is a QObject Q_PROPERTY of + // the given sequence type. Internally we store a typed-sequence + // (as well as object ptr + property index for updated-read and write-back) + // and so access/mutate avoids variant conversion. + *succeeded = true; + FOREACH_QML_SEQUENCE_TYPE(NEW_REFERENCE_SEQUENCE) { /* else */ *succeeded = false; return QV4::Value::undefinedValue(); } +} +#undef NEW_REFERENCE_SEQUENCE + +#define NEW_COPY_SEQUENCE(ElementType, ElementTypeName, SequenceType, unused) \ + if (sequenceType == qMetaTypeId()) { \ + QV4::Object *obj = new (engine->memoryManager) QQml##ElementTypeName##List(engine, v.value()); \ + return QV4::Value::fromObject(obj); \ + } else + +QV4::Value SequencePrototype::fromVariant(QV4::ExecutionEngine *engine, const QVariant& v, bool *succeeded) +{ + // This function is called when assigning a sequence value to a normal JS var + // in a JS block. Internally, we store a sequence of the specified type. + // Access and mutation is extremely fast since it will not need to modify any + // QObject property. + int sequenceType = v.userType(); + *succeeded = true; + FOREACH_QML_SEQUENCE_TYPE(NEW_COPY_SEQUENCE) { /* else */ *succeeded = false; return QV4::Value::undefinedValue(); } +} +#undef NEW_COPY_SEQUENCE + +#define SEQUENCE_TO_VARIANT(ElementType, ElementTypeName, SequenceType, unused) \ + if (QQml##ElementTypeName##List *list = object->as()) \ + return list->toVariant(); \ + else + +QVariant SequencePrototype::toVariant(QV4::Object *object) +{ + Q_ASSERT(object->isListType()); + FOREACH_QML_SEQUENCE_TYPE(SEQUENCE_TO_VARIANT) { /* else */ return QVariant(); } +} + +#define SEQUENCE_TO_VARIANT(ElementType, ElementTypeName, SequenceType, unused) \ + if (typeHint == qMetaTypeId()) { \ + return QQml##ElementTypeName##List::toVariant(a); \ + } else + +QVariant SequencePrototype::toVariant(const QV4::Value &array, int typeHint, bool *succeeded) +{ + *succeeded = true; + + QV4::ArrayObject *a = array.asArrayObject(); + if (!a) { + *succeeded = false; + return QVariant(); + } + FOREACH_QML_SEQUENCE_TYPE(SEQUENCE_TO_VARIANT) { /* else */ *succeeded = false; return QVariant(); } +} + +#undef SEQUENCE_TO_VARIANT + +#define MAP_META_TYPE(ElementType, ElementTypeName, SequenceType, unused) \ + if (object->as()) { \ + return qMetaTypeId(); \ + } else + +int SequencePrototype::metaTypeForSequence(QV4::Object *object) +{ + FOREACH_QML_SEQUENCE_TYPE(MAP_META_TYPE) + /*else*/ { + return -1; + } +} + +#undef MAP_META_TYPE + +QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4sequenceobject_p.h b/src/qml/jsruntime/qv4sequenceobject_p.h new file mode 100644 index 0000000000..2cade45092 --- /dev/null +++ b/src/qml/jsruntime/qv4sequenceobject_p.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4SEQUENCEWRAPPER_P_H +#define QV4SEQUENCEWRAPPER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +#include "qv4value_p.h" +#include "qv4object_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct SequencePrototype : public QV4::Object +{ + SequencePrototype(QV4::ExecutionEngine *engine); + + void init(QV4::ExecutionEngine *engine); + + static QV4::Value method_valueOf(QV4::SimpleCallContext *ctx) + { + return QV4::Value::fromString(ctx->thisObject.toString(ctx)); + } + + static QV4::Value method_sort(QV4::SimpleCallContext *ctx); + + static bool isSequenceType(int sequenceTypeId); + static QV4::Value newSequence(QV4::ExecutionEngine *engine, int sequenceTypeId, QObject *object, int propertyIndex, bool *succeeded); + static QV4::Value fromVariant(QV4::ExecutionEngine *engine, const QVariant& v, bool *succeeded); + static int metaTypeForSequence(QV4::Object *object); + static QVariant toVariant(QV4::Object *object); + static QVariant toVariant(const QV4::Value &array, int typeHint, bool *succeeded); +}; + +} + +QT_END_NAMESPACE + +#endif // QV4SEQUENCEWRAPPER_P_H diff --git a/src/qml/jsruntime/qv4serialize.cpp b/src/qml/jsruntime/qv4serialize.cpp new file mode 100644 index 0000000000..f7389dc6d7 --- /dev/null +++ b/src/qml/jsruntime/qv4serialize.cpp @@ -0,0 +1,399 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4serialize_p.h" + +#include +#include +#include + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +using namespace QV4; + +// We allow the following JavaScript types to be passed between the main and +// the secondary thread: +// + undefined +// + null +// + Boolean +// + String +// + Function +// + Array +// + "Simple" Objects +// + Number +// + Date +// + RegExp +// + +enum Type { + WorkerUndefined, + WorkerNull, + WorkerTrue, + WorkerFalse, + WorkerString, + WorkerFunction, + WorkerArray, + WorkerObject, + WorkerInt32, + WorkerUint32, + WorkerNumber, + WorkerDate, + WorkerRegexp, + WorkerListModel, + WorkerSequence +}; + +static inline quint32 valueheader(Type type, quint32 size = 0) +{ + return quint8(type) << 24 | (size & 0xFFFFFF); +} + +static inline Type headertype(quint32 header) +{ + return (Type)(header >> 24); +} + +static inline quint32 headersize(quint32 header) +{ + return header & 0xFFFFFF; +} + +static inline void push(QByteArray &data, quint32 value) +{ + data.append((const char *)&value, sizeof(quint32)); +} + +static inline void push(QByteArray &data, double value) +{ + data.append((const char *)&value, sizeof(double)); +} + +static inline void push(QByteArray &data, void *ptr) +{ + data.append((const char *)&ptr, sizeof(void *)); +} + +static inline void reserve(QByteArray &data, int extra) +{ + data.reserve(data.size() + extra); +} + +static inline quint32 popUint32(const char *&data) +{ + quint32 rv = *((quint32 *)data); + data += sizeof(quint32); + return rv; +} + +static inline double popDouble(const char *&data) +{ + double rv = *((double *)data); + data += sizeof(double); + return rv; +} + +static inline void *popPtr(const char *&data) +{ + void *rv = *((void **)data); + data += sizeof(void *); + return rv; +} + +// XXX TODO: Check that worker script is exception safe in the case of +// serialization/deserialization failures + +#define ALIGN(size) (((size) + 3) & ~3) +void Serialize::serialize(QByteArray &data, const QV4::Value &v, QV8Engine *engine) +{ + QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine); + if (v.isEmpty()) { + } else if (v.isUndefined()) { + push(data, valueheader(WorkerUndefined)); + } else if (v.isNull()) { + push(data, valueheader(WorkerNull)); + } else if (v.isBoolean()) { + push(data, valueheader(v.booleanValue() == true ? WorkerTrue : WorkerFalse)); + } else if (QV4::String *s = v.asString()) { + const QString &qstr = s->toQString(); + int length = qstr.length(); + if (length > 0xFFFFFF) { + push(data, valueheader(WorkerUndefined)); + return; + } + int utf16size = ALIGN(length * sizeof(uint16_t)); + + reserve(data, utf16size + sizeof(quint32)); + push(data, valueheader(WorkerString, length)); + + int offset = data.size(); + data.resize(data.size() + utf16size); + char *buffer = data.data() + offset; + + memcpy(buffer, qstr.constData(), length*sizeof(QChar)); + } else if (v.asFunctionObject()) { + // XXX TODO: Implement passing function objects between the main and + // worker scripts + push(data, valueheader(WorkerUndefined)); + } else if (QV4::ArrayObject *array = v.asArrayObject()) { + uint32_t length = array->arrayLength(); + if (length > 0xFFFFFF) { + push(data, valueheader(WorkerUndefined)); + return; + } + reserve(data, sizeof(quint32) + length * sizeof(quint32)); + push(data, valueheader(WorkerArray, length)); + for (uint32_t ii = 0; ii < length; ++ii) + serialize(data, array->getIndexed(ii), engine); + } else if (v.isInteger()) { + reserve(data, 2 * sizeof(quint32)); + push(data, valueheader(WorkerInt32)); + push(data, (quint32)v.integerValue()); +// } else if (v->IsUint32()) { +// reserve(data, 2 * sizeof(quint32)); +// push(data, valueheader(WorkerUint32)); +// push(data, v->Uint32Value()); + } else if (v.isNumber()) { + reserve(data, sizeof(quint32) + sizeof(double)); + push(data, valueheader(WorkerNumber)); + push(data, v.asDouble()); + } else if (QV4::DateObject *d = v.asDateObject()) { + reserve(data, sizeof(quint32) + sizeof(double)); + push(data, valueheader(WorkerDate)); + push(data, d->value.asDouble()); + } else if (QV4::RegExpObject *re = v.as()) { + quint32 flags = re->flags(); + QString pattern = re->source(); + int length = pattern.length() + 1; + if (length > 0xFFFFFF) { + push(data, valueheader(WorkerUndefined)); + return; + } + int utf16size = ALIGN(length * sizeof(uint16_t)); + + reserve(data, sizeof(quint32) + utf16size); + push(data, valueheader(WorkerRegexp, flags)); + push(data, (quint32)length); + + int offset = data.size(); + data.resize(data.size() + utf16size); + char *buffer = data.data() + offset; + + memcpy(buffer, pattern.constData(), length*sizeof(QChar)); + } else if (QV4::QObjectWrapper *qobjectWrapper = v.as()) { + // XXX TODO: Generalize passing objects between the main thread and worker scripts so + // that others can trivially plug in their elements. + QQmlListModel *lm = qobject_cast(qobjectWrapper->object()); + if (lm && lm->agent()) { + QQmlListModelWorkerAgent *agent = lm->agent(); + agent->addref(); + push(data, valueheader(WorkerListModel)); + push(data, (void *)agent); + return; + } + // No other QObject's are allowed to be sent + push(data, valueheader(WorkerUndefined)); + } else if (QV4::Object *o = v.asObject()) { + + if (o->isListType()) { + // valid sequence. we generate a length (sequence length + 1 for the sequence type) + uint32_t seqLength = o->get(v4->id_length).toUInt32(); + uint32_t length = seqLength + 1; + if (length > 0xFFFFFF) { + push(data, valueheader(WorkerUndefined)); + return; + } + reserve(data, sizeof(quint32) + length * sizeof(quint32)); + push(data, valueheader(WorkerSequence, length)); + serialize(data, QV4::Value::fromInt32(QV4::SequencePrototype::metaTypeForSequence(o)), engine); // sequence type + for (uint32_t ii = 0; ii < seqLength; ++ii) + serialize(data, o->getIndexed(ii), engine); // sequence elements + + return; + } + + // regular object + QV4::ArrayObject *properties = QV4::ObjectPrototype::getOwnPropertyNames(v4, v); + quint32 length = properties->arrayLength(); + if (length > 0xFFFFFF) { + push(data, valueheader(WorkerUndefined)); + return; + } + push(data, valueheader(WorkerObject, length)); + + QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine); + for (quint32 ii = 0; ii < length; ++ii) { + QV4::String *s = properties->getIndexed(ii).asString(); + serialize(data, QV4::Value::fromString(s), engine); + + bool hasCaught = false; + QV4::ExecutionContext *ctx = v4->current; + QV4::Value val = QV4::Value::undefinedValue(); + try { + val = o->get(s); + } catch (QV4::Exception &e) { + e.accept(ctx); + } + + serialize(data, val, engine); + } + return; + } else { + push(data, valueheader(WorkerUndefined)); + } +} + +QV4::Value Serialize::deserialize(const char *&data, QV8Engine *engine) +{ + quint32 header = popUint32(data); + Type type = headertype(header); + + QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine); + + switch (type) { + case WorkerUndefined: + return QV4::Value::undefinedValue(); + case WorkerNull: + return QV4::Value::nullValue(); + case WorkerTrue: + return QV4::Value::fromBoolean(true); + case WorkerFalse: + return QV4::Value::fromBoolean(false); + case WorkerString: + { + quint32 size = headersize(header); + QString qstr((QChar *)data, size); + data += ALIGN(size * sizeof(uint16_t)); + return QV4::Value::fromString(v4->newString(qstr)); + } + case WorkerFunction: + Q_ASSERT(!"Unreachable"); + break; + case WorkerArray: + { + quint32 size = headersize(header); + QV4::ArrayObject *a = v4->newArrayObject(); + for (quint32 ii = 0; ii < size; ++ii) { + a->putIndexed(ii, deserialize(data, engine)); + } + return QV4::Value::fromObject(a); + } + case WorkerObject: + { + quint32 size = headersize(header); + QV4::Object *o = v4->newObject(); + for (quint32 ii = 0; ii < size; ++ii) { + QV4::Value name = deserialize(data, engine); + QV4::Value value = deserialize(data, engine); + o->put(name.asString(), value); + } + return QV4::Value::fromObject(o); + } + case WorkerInt32: + return QV4::Value::fromInt32((qint32)popUint32(data)); + case WorkerUint32: + return QV4::Value::fromUInt32(popUint32(data)); + case WorkerNumber: + return QV4::Value::fromDouble(popDouble(data)); + case WorkerDate: + return QV4::Value::fromObject(v4->newDateObject(QV4::Value::fromDouble(popDouble(data)))); + case WorkerRegexp: + { + quint32 flags = headersize(header); + quint32 length = popUint32(data); + QString pattern = QString((QChar *)data, length - 1); + data += ALIGN(length * sizeof(uint16_t)); + return QV4::Value::fromObject(v4->newRegExpObject(pattern, flags)); + } + case WorkerListModel: + { + void *ptr = popPtr(data); + QQmlListModelWorkerAgent *agent = (QQmlListModelWorkerAgent *)ptr; + QV4::Value rv = QV4::QObjectWrapper::wrap(v4, agent); + // ### Find a better solution then the ugly property + QQmlListModelWorkerAgent::VariantRef ref(agent); + QVariant var = qVariantFromValue(ref); + rv.asObject()->defineReadonlyProperty(v4->newString("__qml:hidden:ref"), engine->fromVariant(var)); + + agent->release(); + agent->setV8Engine(engine); + return rv; + } + case WorkerSequence: + { + bool succeeded = false; + quint32 length = headersize(header); + quint32 seqLength = length - 1; + int sequenceType = deserialize(data, engine).integerValue(); + QV4::ArrayObject *array = v4->newArrayObject(); + array->arrayReserve(seqLength); + array->arrayDataLen = seqLength; + for (quint32 ii = 0; ii < seqLength; ++ii) + array->arrayData[ii].value = deserialize(data, engine); + array->setArrayLengthUnchecked(seqLength); + QVariant seqVariant = QV4::SequencePrototype::toVariant(QV4::Value::fromObject(array), sequenceType, &succeeded); + return QV4::SequencePrototype::fromVariant(v4, seqVariant, &succeeded); + } + } + Q_ASSERT(!"Unreachable"); + return QV4::Value::undefinedValue(); +} + +QByteArray Serialize::serialize(const QV4::Value &value, QV8Engine *engine) +{ + QByteArray rv; + serialize(rv, value, engine); + return rv; +} + +QV4::Value Serialize::deserialize(const QByteArray &data, QV8Engine *engine) +{ + const char *stream = data.constData(); + return deserialize(stream, engine); +} + +QT_END_NAMESPACE + diff --git a/src/qml/jsruntime/qv4serialize_p.h b/src/qml/jsruntime/qv4serialize_p.h new file mode 100644 index 0000000000..5a04c9d25f --- /dev/null +++ b/src/qml/jsruntime/qv4serialize_p.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4SERIALIZE_P_H +#define QV4SERIALIZE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +QT_BEGIN_NAMESPACE + +class QV8Engine; + +namespace QV4 { + +class Serialize { +public: + + static QByteArray serialize(const Value &, QV8Engine *); + static Value deserialize(const QByteArray &, QV8Engine *); + +private: + static void serialize(QByteArray &, const Value &, QV8Engine *); + static Value deserialize(const char *&, QV8Engine *); +}; + +} + +QT_END_NAMESPACE + +#endif // QV8WORKER_P_H diff --git a/src/qml/jsruntime/qv4sparsearray.cpp b/src/qml/jsruntime/qv4sparsearray.cpp new file mode 100644 index 0000000000..835a0d004f --- /dev/null +++ b/src/qml/jsruntime/qv4sparsearray.cpp @@ -0,0 +1,459 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4sparsearray_p.h" +#include "qv4runtime_p.h" +#include "qv4object_p.h" +#include "qv4functionobject_p.h" +#include + +#ifdef QT_QMAP_DEBUG +# include +# include +#endif + +using namespace QV4; + +bool ArrayElementLessThan::operator()(const Property &p1, const Property &p2) const +{ + Value v1 = p1.value; + Value v2 = p2.value; + + if (v1.isUndefined()) + return false; + if (v2.isUndefined()) + return true; + if (!m_comparefn.isUndefined()) { + Value args[] = { v1, v2 }; + Value result = Value::undefinedValue(); + __qmljs_call_value(m_context, &result, /*thisObject*/0, m_comparefn, args, 2); + return result.toNumber() <= 0; + } + return v1.toString(m_context)->toQString() < v2.toString(m_context)->toQString(); +} + + +const SparseArrayNode *SparseArrayNode::nextNode() const +{ + const SparseArrayNode *n = this; + if (n->right) { + n = n->right; + while (n->left) + n = n->left; + } else { + const SparseArrayNode *y = n->parent(); + while (y && n == y->right) { + n = y; + y = n->parent(); + } + n = y; + } + return n; +} + +const SparseArrayNode *SparseArrayNode::previousNode() const +{ + const SparseArrayNode *n = this; + if (n->left) { + n = n->left; + while (n->right) + n = n->right; + } else { + const SparseArrayNode *y = n->parent(); + while (y && n == y->left) { + n = y; + y = n->parent(); + } + n = y; + } + return n; +} + +SparseArrayNode *SparseArrayNode::copy(SparseArray *d) const +{ + SparseArrayNode *n = d->createNode(size_left, 0, false); + n->value = value; + n->setColor(color()); + if (left) { + n->left = left->copy(d); + n->left->setParent(n); + } else { + n->left = 0; + } + if (right) { + n->right = right->copy(d); + n->right->setParent(n); + } else { + n->right = 0; + } + return n; +} + +/* + x y + \ / \ + y --> x b + / \ \ + a b a +*/ +void SparseArray::rotateLeft(SparseArrayNode *x) +{ + SparseArrayNode *&root = header.left; + SparseArrayNode *y = x->right; + x->right = y->left; + if (y->left != 0) + y->left->setParent(x); + y->setParent(x->parent()); + if (x == root) + root = y; + else if (x == x->parent()->left) + x->parent()->left = y; + else + x->parent()->right = y; + y->left = x; + x->setParent(y); + y->size_left += x->size_left; +} + + +/* + x y + / / \ + y --> a x + / \ / + a b b +*/ +void SparseArray::rotateRight(SparseArrayNode *x) +{ + SparseArrayNode *&root = header.left; + SparseArrayNode *y = x->left; + x->left = y->right; + if (y->right != 0) + y->right->setParent(x); + y->setParent(x->parent()); + if (x == root) + root = y; + else if (x == x->parent()->right) + x->parent()->right = y; + else + x->parent()->left = y; + y->right = x; + x->setParent(y); + x->size_left -= y->size_left; +} + + +void SparseArray::rebalance(SparseArrayNode *x) +{ + SparseArrayNode *&root = header.left; + x->setColor(SparseArrayNode::Red); + while (x != root && x->parent()->color() == SparseArrayNode::Red) { + if (x->parent() == x->parent()->parent()->left) { + SparseArrayNode *y = x->parent()->parent()->right; + if (y && y->color() == SparseArrayNode::Red) { + x->parent()->setColor(SparseArrayNode::Black); + y->setColor(SparseArrayNode::Black); + x->parent()->parent()->setColor(SparseArrayNode::Red); + x = x->parent()->parent(); + } else { + if (x == x->parent()->right) { + x = x->parent(); + rotateLeft(x); + } + x->parent()->setColor(SparseArrayNode::Black); + x->parent()->parent()->setColor(SparseArrayNode::Red); + rotateRight (x->parent()->parent()); + } + } else { + SparseArrayNode *y = x->parent()->parent()->left; + if (y && y->color() == SparseArrayNode::Red) { + x->parent()->setColor(SparseArrayNode::Black); + y->setColor(SparseArrayNode::Black); + x->parent()->parent()->setColor(SparseArrayNode::Red); + x = x->parent()->parent(); + } else { + if (x == x->parent()->left) { + x = x->parent(); + rotateRight(x); + } + x->parent()->setColor(SparseArrayNode::Black); + x->parent()->parent()->setColor(SparseArrayNode::Red); + rotateLeft(x->parent()->parent()); + } + } + } + root->setColor(SparseArrayNode::Black); +} + +void SparseArray::deleteNode(SparseArrayNode *z) +{ + SparseArrayNode *&root = header.left; + SparseArrayNode *y = z; + SparseArrayNode *x; + SparseArrayNode *x_parent; + if (y->left == 0) { + x = y->right; + if (y == mostLeftNode) { + if (x) + mostLeftNode = x; // It cannot have (left) children due the red black invariant. + else + mostLeftNode = y->parent(); + } + } else { + if (y->right == 0) { + x = y->left; + } else { + y = y->right; + while (y->left != 0) + y = y->left; + x = y->right; + } + } + if (y != z) { + z->left->setParent(y); + y->left = z->left; + if (y != z->right) { + x_parent = y->parent(); + if (x) + x->setParent(y->parent()); + y->parent()->left = x; + y->right = z->right; + z->right->setParent(y); + } else { + x_parent = y; + } + if (root == z) + root = y; + else if (z->parent()->left == z) + z->parent()->left = y; + else + z->parent()->right = y; + y->setParent(z->parent()); + // Swap the colors + SparseArrayNode::Color c = y->color(); + y->setColor(z->color()); + z->setColor(c); + y = z; + } else { + x_parent = y->parent(); + if (x) + x->setParent(y->parent()); + if (root == z) + root = x; + else if (z->parent()->left == z) + z->parent()->left = x; + else + z->parent()->right = x; + } + if (y->color() != SparseArrayNode::Red) { + while (x != root && (x == 0 || x->color() == SparseArrayNode::Black)) { + if (x == x_parent->left) { + SparseArrayNode *w = x_parent->right; + if (w->color() == SparseArrayNode::Red) { + w->setColor(SparseArrayNode::Black); + x_parent->setColor(SparseArrayNode::Red); + rotateLeft(x_parent); + w = x_parent->right; + } + if ((w->left == 0 || w->left->color() == SparseArrayNode::Black) && + (w->right == 0 || w->right->color() == SparseArrayNode::Black)) { + w->setColor(SparseArrayNode::Red); + x = x_parent; + x_parent = x_parent->parent(); + } else { + if (w->right == 0 || w->right->color() == SparseArrayNode::Black) { + if (w->left) + w->left->setColor(SparseArrayNode::Black); + w->setColor(SparseArrayNode::Red); + rotateRight(w); + w = x_parent->right; + } + w->setColor(x_parent->color()); + x_parent->setColor(SparseArrayNode::Black); + if (w->right) + w->right->setColor(SparseArrayNode::Black); + rotateLeft(x_parent); + break; + } + } else { + SparseArrayNode *w = x_parent->left; + if (w->color() == SparseArrayNode::Red) { + w->setColor(SparseArrayNode::Black); + x_parent->setColor(SparseArrayNode::Red); + rotateRight(x_parent); + w = x_parent->left; + } + if ((w->right == 0 || w->right->color() == SparseArrayNode::Black) && + (w->left == 0 || w->left->color() == SparseArrayNode::Black)) { + w->setColor(SparseArrayNode::Red); + x = x_parent; + x_parent = x_parent->parent(); + } else { + if (w->left == 0 || w->left->color() == SparseArrayNode::Black) { + if (w->right) + w->right->setColor(SparseArrayNode::Black); + w->setColor(SparseArrayNode::Red); + rotateLeft(w); + w = x_parent->left; + } + w->setColor(x_parent->color()); + x_parent->setColor(SparseArrayNode::Black); + if (w->left) + w->left->setColor(SparseArrayNode::Black); + rotateRight(x_parent); + break; + } + } + } + if (x) + x->setColor(SparseArrayNode::Black); + } + free(y); + --numEntries; +} + +void SparseArray::recalcMostLeftNode() +{ + mostLeftNode = &header; + while (mostLeftNode->left) + mostLeftNode = mostLeftNode->left; +} + +static inline int qMapAlignmentThreshold() +{ + // malloc on 32-bit platforms should return pointers that are 8-byte + // aligned or more while on 64-bit platforms they should be 16-byte aligned + // or more + return 2 * sizeof(void*); +} + +static inline void *qMapAllocate(int alloc, int alignment) +{ + return alignment > qMapAlignmentThreshold() + ? qMallocAligned(alloc, alignment) + : ::malloc(alloc); +} + +static inline void qMapDeallocate(SparseArrayNode *node, int alignment) +{ + if (alignment > qMapAlignmentThreshold()) + qFreeAligned(node); + else + ::free(node); +} + +SparseArrayNode *SparseArray::createNode(uint sl, SparseArrayNode *parent, bool left) +{ + SparseArrayNode *node = static_cast(qMapAllocate(sizeof(SparseArrayNode), Q_ALIGNOF(SparseArrayNode))); + Q_CHECK_PTR(node); + + node->p = (quintptr)parent; + node->left = 0; + node->right = 0; + node->size_left = sl; + node->value = UINT_MAX; + ++numEntries; + + if (parent) { + if (left) { + parent->left = node; + if (parent == mostLeftNode) + mostLeftNode = node; + } else { + parent->right = node; + } + node->setParent(parent); + rebalance(node); + } + return node; +} + +void SparseArray::freeTree(SparseArrayNode *root, int alignment) +{ + if (root->left) + freeTree(root->left, alignment); + if (root->right) + freeTree(root->right, alignment); + qMapDeallocate(root, alignment); +} + +SparseArray::SparseArray() + : numEntries(0) +{ + header.p = 0; + header.left = 0; + header.right = 0; + mostLeftNode = &header; +} + +SparseArray::SparseArray(const SparseArray &other) +{ + header.p = 0; + header.right = 0; + if (other.header.left) { + header.left = other.header.left->copy(this); + header.left->setParent(&header); + recalcMostLeftNode(); + } +} + +SparseArrayNode *SparseArray::insert(uint akey) +{ + SparseArrayNode *n = root(); + SparseArrayNode *y = end(); + bool left = true; + uint s = akey; + while (n) { + y = n; + if (s == n->size_left) { + return n; + } else if (s < n->size_left) { + left = true; + n = n->left; + } else { + left = false; + s -= n->size_left; + n = n->right; + } + } + + return createNode(s, y, left); +} diff --git a/src/qml/jsruntime/qv4sparsearray_p.h b/src/qml/jsruntime/qv4sparsearray_p.h new file mode 100644 index 0000000000..384d2ef045 --- /dev/null +++ b/src/qml/jsruntime/qv4sparsearray_p.h @@ -0,0 +1,367 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4ARRAY_H +#define QV4ARRAY_H + +#include "qv4global_p.h" +#include +#include "qv4value_p.h" +#include "qv4property_p.h" +#include + +#ifdef Q_MAP_DEBUG +#include +#endif + +#include + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct SparseArray; + +class ArrayElementLessThan +{ +public: + inline ArrayElementLessThan(ExecutionContext *context, Object *thisObject, const Value &comparefn) + : m_context(context), thisObject(thisObject), m_comparefn(comparefn) {} + + bool operator()(const Property &v1, const Property &v2) const; + +private: + ExecutionContext *m_context; + Object *thisObject; + Value m_comparefn; +}; + + +struct SparseArrayNode +{ + quintptr p; + SparseArrayNode *left; + SparseArrayNode *right; + uint size_left; + uint value; + + enum Color { Red = 0, Black = 1 }; + enum { Mask = 3 }; // reserve the second bit as well + + const SparseArrayNode *nextNode() const; + SparseArrayNode *nextNode() { return const_cast(const_cast(this)->nextNode()); } + const SparseArrayNode *previousNode() const; + SparseArrayNode *previousNode() { return const_cast(const_cast(this)->previousNode()); } + + Color color() const { return Color(p & 1); } + void setColor(Color c) { if (c == Black) p |= Black; else p &= ~Black; } + SparseArrayNode *parent() const { return reinterpret_cast(p & ~Mask); } + void setParent(SparseArrayNode *pp) { p = (p & Mask) | quintptr(pp); } + + uint key() const { + uint k = size_left; + const SparseArrayNode *n = this; + while (SparseArrayNode *p = n->parent()) { + if (p && p->right == n) + k += p->size_left; + n = p; + } + return k; + } + + SparseArrayNode *copy(SparseArray *d) const; + + SparseArrayNode *lowerBound(uint key); + SparseArrayNode *upperBound(uint key); +}; + + +inline SparseArrayNode *SparseArrayNode::lowerBound(uint akey) +{ + SparseArrayNode *n = this; + SparseArrayNode *last = 0; + while (n) { + if (akey <= n->size_left) { + last = n; + n = n->left; + } else { + akey -= n->size_left; + n = n->right; + } + } + return last; +} + + +inline SparseArrayNode *SparseArrayNode::upperBound(uint akey) +{ + SparseArrayNode *n = this; + SparseArrayNode *last = 0; + while (n) { + if (akey < n->size_left) { + last = n; + n = n->left; + } else { + akey -= n->size_left; + n = n->right; + } + } + return last; +} + + + +struct Q_QML_EXPORT SparseArray +{ + SparseArray(); + ~SparseArray() { + if (root()) + freeTree(header.left, Q_ALIGNOF(SparseArrayNode)); + } + + SparseArray(const SparseArray &other); +private: + SparseArray &operator=(const SparseArray &other); + + int numEntries; + SparseArrayNode header; + SparseArrayNode *mostLeftNode; + + void rotateLeft(SparseArrayNode *x); + void rotateRight(SparseArrayNode *x); + void rebalance(SparseArrayNode *x); + void recalcMostLeftNode(); + + SparseArrayNode *root() const { return header.left; } + + void deleteNode(SparseArrayNode *z); + + +public: + SparseArrayNode *createNode(uint sl, SparseArrayNode *parent, bool left); + void freeTree(SparseArrayNode *root, int alignment); + + SparseArrayNode *findNode(uint akey) const; + + uint pop_front(); + void push_front(uint at); + uint pop_back(uint len); + void push_back(uint at, uint len); + + QList keys() const; + + const SparseArrayNode *end() const { return &header; } + SparseArrayNode *end() { return &header; } + const SparseArrayNode *begin() const { if (root()) return mostLeftNode; return end(); } + SparseArrayNode *begin() { if (root()) return mostLeftNode; return end(); } + + SparseArrayNode *erase(SparseArrayNode *n); + + SparseArrayNode *lowerBound(uint key); + const SparseArrayNode *lowerBound(uint key) const; + SparseArrayNode *upperBound(uint key); + const SparseArrayNode *upperBound(uint key) const; + SparseArrayNode *insert(uint akey); + + // STL compatibility + typedef uint key_type; + typedef int mapped_type; + typedef qptrdiff difference_type; + typedef int size_type; + +#ifdef Q_MAP_DEBUG + void dump() const; +#endif +}; + +inline SparseArrayNode *SparseArray::findNode(uint akey) const +{ + SparseArrayNode *n = root(); + + while (n) { + if (akey == n->size_left) { + return n; + } else if (akey < n->size_left) { + n = n->left; + } else { + akey -= n->size_left; + n = n->right; + } + } + + return 0; +} + +inline uint SparseArray::pop_front() +{ + uint idx = UINT_MAX ; + + SparseArrayNode *n = findNode(0); + if (n) { + idx = n->value; + deleteNode(n); + // adjust all size_left indices on the path to leftmost item by 1 + SparseArrayNode *n = root(); + while (n) { + n->size_left -= 1; + n = n->left; + } + } + return idx; +} + +inline void SparseArray::push_front(uint value) +{ + // adjust all size_left indices on the path to leftmost item by 1 + SparseArrayNode *n = root(); + while (n) { + n->size_left += 1; + n = n->left; + } + n = insert(0); + n->value = value; +} + +inline uint SparseArray::pop_back(uint len) +{ + uint idx = UINT_MAX; + if (!len) + return idx; + + SparseArrayNode *n = findNode(len - 1); + if (n) { + idx = n->value; + deleteNode(n); + } + return idx; +} + +inline void SparseArray::push_back(uint index, uint len) +{ + SparseArrayNode *n = insert(len); + n->value = index; +} + +#ifdef Q_MAP_DEBUG + +void SparseArray::dump() const +{ + const_iterator it = begin(); + qDebug() << "map dump:"; + while (it != end()) { + const SparseArrayNode *n = it.i; + int depth = 0; + while (n && n != root()) { + ++depth; + n = n->parent(); + } + QByteArray space(4*depth, ' '); + qDebug() << space << (it.i->color() == SparseArrayNode::Red ? "Red " : "Black") << it.i << it.i->left << it.i->right + << it.key() << it.value(); + ++it; + } + qDebug() << "---------"; +} +#endif + + +inline SparseArrayNode *SparseArray::erase(SparseArrayNode *n) +{ + if (n == end()) + return n; + + SparseArrayNode *next = n->nextNode(); + deleteNode(n); + return next; +} + +inline QList SparseArray::keys() const +{ + QList res; + res.reserve(numEntries); + SparseArrayNode *n = mostLeftNode; + while (n != end()) { + res.append(n->key()); + n = n->nextNode(); + } + return res; +} + +inline const SparseArrayNode *SparseArray::lowerBound(uint akey) const +{ + const SparseArrayNode *lb = root()->lowerBound(akey); + if (!lb) + lb = end(); + return lb; +} + + +inline SparseArrayNode *SparseArray::lowerBound(uint akey) +{ + SparseArrayNode *lb = root()->lowerBound(akey); + if (!lb) + lb = end(); + return lb; +} + + +inline const SparseArrayNode *SparseArray::upperBound(uint akey) const +{ + const SparseArrayNode *ub = root()->upperBound(akey); + if (!ub) + ub = end(); + return ub; +} + + +inline SparseArrayNode *SparseArray::upperBound(uint akey) +{ + SparseArrayNode *ub = root()->upperBound(akey); + if (!ub) + ub = end(); + return ub; +} + +} + +QT_END_NAMESPACE + +#endif // QMAP_H diff --git a/src/qml/jsruntime/qv4stacktrace.cpp b/src/qml/jsruntime/qv4stacktrace.cpp new file mode 100644 index 0000000000..1cc2e53556 --- /dev/null +++ b/src/qml/jsruntime/qv4stacktrace.cpp @@ -0,0 +1,146 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#if defined(_WIN32) +#include +#include +#endif + +#include "qv4stacktrace_p.h" +#include "qv4function_p.h" +#include "qv4engine_p.h" +#include "qv4unwindhelper_p.h" + +#if (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)) || defined(Q_OS_MAC) +#define HAVE_GNU_BACKTRACE +#include +#endif + +QT_BEGIN_NAMESPACE + +using namespace QV4; + +NativeStackTrace::NativeStackTrace(ExecutionContext *context) +{ + engine = context->engine; + currentNativeFrame = 0; + +#if defined(HAVE_GNU_BACKTRACE) + UnwindHelper::prepareForUnwind(context); + + nativeFrameCount = backtrace(&trace[0], sizeof(trace) / sizeof(trace[0])); +#elif defined(Q_OS_WIN) + + int machineType = 0; + + CONTEXT winContext; + memset(&winContext, 0, sizeof(winContext)); + winContext.ContextFlags = CONTEXT_FULL; + RtlCaptureContext(&winContext); + + STACKFRAME64 sf64; + memset(&sf64, 0, sizeof(sf64)); + +#if defined(Q_PROCESSOR_X86_32) + machineType = IMAGE_FILE_MACHINE_I386; + + sf64.AddrFrame.Offset = winContext.Ebp; + sf64.AddrFrame.Mode = AddrModeFlat; + sf64.AddrPC.Offset = winContext.Eip; + sf64.AddrPC.Mode = AddrModeFlat; + sf64.AddrStack.Offset = winContext.Esp; + sf64.AddrStack.Mode = AddrModeFlat; + +#elif defined(Q_PROCESSOR_X86_64) + machineType = IMAGE_FILE_MACHINE_AMD64; + + sf64.AddrFrame.Offset = winContext.Rbp; + sf64.AddrFrame.Mode = AddrModeFlat; + sf64.AddrPC.Offset = winContext.Rip; + sf64.AddrPC.Mode = AddrModeFlat; + sf64.AddrStack.Offset = winContext.Rsp; + sf64.AddrStack.Mode = AddrModeFlat; + +#else +#error "Platform unsupported!" +#endif + + nativeFrameCount = 0; + + while (StackWalk64(machineType, GetCurrentProcess(), GetCurrentThread(), &sf64, &winContext, 0, SymFunctionTableAccess64, SymGetModuleBase64, 0)) { + + if (sf64.AddrReturn.Offset == 0) + break; + + trace[nativeFrameCount] = reinterpret_cast(sf64.AddrReturn.Offset); + nativeFrameCount++; + if (nativeFrameCount >= sizeof(trace) / sizeof(trace[0])) + break; + } + +#else + nativeFrameCount = 0; +#endif + } + +NativeFrame NativeStackTrace::nextFrame() { + NativeFrame frame; + frame.function = 0; + frame.line = -1; + + for (; currentNativeFrame < nativeFrameCount && !frame.function; ++currentNativeFrame) { + quintptr pc = reinterpret_cast(trace[currentNativeFrame]); + // The pointers from the back trace point to the return address, but we are interested in + // the caller site. + pc = pc - 1; + + Function *f = engine->functionForProgramCounter(pc); + if (!f) + continue; + + frame.function = f; + frame.line = f->lineNumberForProgramCounter(pc - reinterpret_cast(f->code)); + } + + return frame; +} + +QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4stacktrace_p.h b/src/qml/jsruntime/qv4stacktrace_p.h new file mode 100644 index 0000000000..79cb4d1813 --- /dev/null +++ b/src/qml/jsruntime/qv4stacktrace_p.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4STACKTRACE_P_H +#define QV4STACKTRACE_P_H + +#include + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct Function; +struct ExecutionEngine; +struct ExecutionContext; + +struct NativeFrame { + Function *function; + int line; +}; + +struct NativeStackTrace +{ + void *trace[100]; + int nativeFrameCount; + int currentNativeFrame; + ExecutionEngine *engine; + + NativeStackTrace(ExecutionContext *context); + + NativeFrame nextFrame(); +}; + +} // namespace QV4 + +QT_END_NAMESPACE + +#endif // QV4STACKTRACE_P_H diff --git a/src/qml/jsruntime/qv4string.cpp b/src/qml/jsruntime/qv4string.cpp new file mode 100644 index 0000000000..8b78c40129 --- /dev/null +++ b/src/qml/jsruntime/qv4string.cpp @@ -0,0 +1,321 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4string_p.h" +#include "qv4identifiertable_p.h" +#include "qv4runtime_p.h" +#include "qv4objectproto_p.h" +#include "qv4stringobject_p.h" +#include + +using namespace QV4; + +static uint toArrayIndex(const QChar *ch, const QChar *end, bool *ok) +{ + *ok = false; + uint i = ch->unicode() - '0'; + if (i > 9) + return UINT_MAX; + ++ch; + // reject "01", "001", ... + if (i == 0 && ch != end) + return UINT_MAX; + + while (ch < end) { + uint x = ch->unicode() - '0'; + if (x > 9) + return UINT_MAX; + uint n = i*10 + x; + if (n < i) + // overflow + return UINT_MAX; + i = n; + ++ch; + } + *ok = true; + return i; +} + +static uint toArrayIndex(const char *ch, const char *end, bool *ok) +{ + *ok = false; + uint i = *ch - '0'; + if (i > 9) + return UINT_MAX; + ++ch; + // reject "01", "001", ... + if (i == 0 && ch != end) + return UINT_MAX; + + while (ch < end) { + uint x = *ch - '0'; + if (x > 9) + return UINT_MAX; + uint n = i*10 + x; + if (n < i) + // overflow + return UINT_MAX; + i = n; + ++ch; + } + *ok = true; + return i; +} + + +const ManagedVTable String::static_vtbl = +{ + call, + construct, + 0 /*markObjects*/, + destroy, + 0 /*collectDeletables*/, + hasInstance, + get, + getIndexed, + put, + putIndexed, + query, + queryIndexed, + deleteProperty, + deleteIndexedProperty, + 0 /*getLookup*/, + 0 /*setLookup*/, + isEqualTo, + 0 /*advanceIterator*/, + "String", +}; + +void String::destroy(Managed *that) +{ + static_cast(that)->~String(); +} + +Value String::get(Managed *m, String *name, bool *hasProperty) +{ + String *that = static_cast(m); + ExecutionEngine *v4 = m->engine(); + if (name == v4->id_length) { + if (hasProperty) + *hasProperty = true; + return Value::fromInt32(that->_text.length()); + } + PropertyAttributes attrs; + Property *pd = v4->stringPrototype->__getPropertyDescriptor__(name, &attrs); + if (!pd || attrs.isGeneric()) { + if (hasProperty) + *hasProperty = false; + return Value::undefinedValue(); + } + if (hasProperty) + *hasProperty = true; + return v4->stringPrototype->getValue(Value::fromString(that), pd, attrs); +} + +Value String::getIndexed(Managed *m, uint index, bool *hasProperty) +{ + String *that = static_cast(m); + ExecutionEngine *engine = that->engine(); + if (index < that->_text.length()) { + if (hasProperty) + *hasProperty = true; + return Value::fromString(engine->newString(that->toQString().mid(index, 1))); + } + PropertyAttributes attrs; + Property *pd = engine->stringPrototype->__getPropertyDescriptor__(index, &attrs); + if (!pd || attrs.isGeneric()) { + if (hasProperty) + *hasProperty = false; + return Value::undefinedValue(); + } + if (hasProperty) + *hasProperty = true; + return engine->stringPrototype->getValue(Value::fromString(that), pd, attrs); +} + +void String::put(Managed *m, String *name, const Value &value) +{ + String *that = static_cast(m); + Object *o = that->engine()->newStringObject(Value::fromString(that)); + o->put(name, value); +} + +void String::putIndexed(Managed *m, uint index, const Value &value) +{ + String *that = static_cast(m); + Object *o = m->engine()->newStringObject(Value::fromString(that)); + o->putIndexed(index, value); +} + +PropertyAttributes String::query(const Managed *m, String *name) +{ + uint idx = name->asArrayIndex(); + if (idx != UINT_MAX) + return queryIndexed(m, idx); + return Attr_Invalid; +} + +PropertyAttributes String::queryIndexed(const Managed *m, uint index) +{ + const String *that = static_cast(m); + return (index < that->_text.length()) ? Attr_NotConfigurable|Attr_NotWritable : Attr_Invalid; +} + +bool String::deleteProperty(Managed *, String *) +{ + return false; +} + +bool String::deleteIndexedProperty(Managed *m, uint index) +{ + return false; +} + +bool String::isEqualTo(Managed *t, Managed *o) +{ + if (t == o) + return true; + String *that = static_cast(t); + String *other = static_cast(o); + if (that->hashValue() != other->hashValue()) + return false; + if (that->identifier && that->identifier == other->identifier) + return true; + if (that->subtype >= StringType_UInt && that->subtype == other->subtype) + return true; + + return that->toQString() == other->toQString(); +} + + +String::String(ExecutionEngine *engine, const QString &text) + : Managed(engine ? engine->emptyClass : 0), _text(text), identifier(0), stringHash(UINT_MAX) +{ + vtbl = &static_vtbl; + type = Type_String; + subtype = StringType_Unknown; +} + +uint String::toUInt(bool *ok) const +{ + *ok = true; + + if (subtype == StringType_Unknown) + createHashValue(); + if (subtype >= StringType_UInt) + return stringHash; + + // ### this conversion shouldn't be required + double d = __qmljs_string_to_number(toQString()); + uint l = (uint)d; + if (d == l) + return l; + *ok = false; + return UINT_MAX; +} + +void String::makeIdentifierImpl() +{ + engine()->identifierTable->identifier(this); +} + +void String::createHashValue() const +{ + const QChar *ch = _text.constData(); + const QChar *end = ch + _text.length(); + + // array indices get their number as hash value + bool ok; + stringHash = toArrayIndex(ch, end, &ok); + if (ok) { + subtype = (stringHash == UINT_MAX) ? StringType_UInt : StringType_ArrayIndex; + return; + } + + uint h = 0xffffffff; + while (ch < end) { + h = 31 * h + ch->unicode(); + ++ch; + } + + stringHash = h; + subtype = StringType_Regular; +} + +uint String::createHashValue(const QChar *ch, int length) +{ + const QChar *end = ch + length; + + // array indices get their number as hash value + bool ok; + uint stringHash = toArrayIndex(ch, end, &ok); + if (ok) + return stringHash; + + uint h = 0xffffffff; + while (ch < end) { + h = 31 * h + ch->unicode(); + ++ch; + } + + return h; +} + +uint String::createHashValue(const char *ch, int length) +{ + const char *end = ch + length; + + // array indices get their number as hash value + bool ok; + uint stringHash = toArrayIndex(ch, end, &ok); + if (ok) + return stringHash; + + uint h = 0xffffffff; + while (ch < end) { + if (*ch >= 0x80) + return UINT_MAX; + h = 31 * h + *ch; + ++ch; + } + + return h; +} diff --git a/src/qml/jsruntime/qv4string_p.h b/src/qml/jsruntime/qv4string_p.h new file mode 100644 index 0000000000..31e5c2a5f7 --- /dev/null +++ b/src/qml/jsruntime/qv4string_p.h @@ -0,0 +1,146 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4STRING_H +#define QV4STRING_H + +#include +#include "qv4managed_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct ExecutionEngine; +struct Identifier; + +struct Q_QML_EXPORT String : public Managed { + enum StringType { + StringType_Unknown, + StringType_Regular, + StringType_UInt, + StringType_ArrayIndex + }; + + String() : Managed(0), identifier(0), stringHash(UINT_MAX) + { vtbl = &static_vtbl; type = Type_String; subtype = StringType_Unknown; } + String(ExecutionEngine *engine, const QString &text); + ~String() { _data = 0; } + + inline bool isEqualTo(const String *other) const { + if (this == other) + return true; + if (hashValue() != other->hashValue()) + return false; + if (identifier && identifier == other->identifier) + return true; + if (subtype >= StringType_UInt && subtype == other->subtype) + return true; + + return toQString() == other->toQString(); + } + inline bool compare(const String *other) { + return toQString() < other->toQString(); + } + + inline bool isEmpty() const { return _text.isEmpty(); } + inline const QString &toQString() const { + return _text; + } + + inline unsigned hashValue() const { + if (subtype == StringType_Unknown) + createHashValue(); + + return stringHash; + } + uint asArrayIndex() const { + if (subtype == StringType_Unknown) + createHashValue(); + if (subtype == StringType_ArrayIndex) + return stringHash; + return UINT_MAX; + } + uint toUInt(bool *ok) const; + + void makeIdentifier() { + if (identifier) + return; + makeIdentifierImpl(); + } + + void makeIdentifierImpl(); + + void createHashValue() const; + static uint createHashValue(const QChar *ch, int length); + static uint createHashValue(const char *ch, int length); + + bool startsWithUpper() const { + return _text.length() && _text.at(0).isUpper(); + } + int length() const { + return _text.length(); + } + + QString _text; + mutable Identifier *identifier; + mutable uint stringHash; + + +protected: + static void destroy(Managed *); + static Value get(Managed *m, String *name, bool *hasProperty); + static Value getIndexed(Managed *m, uint index, bool *hasProperty); + static void put(Managed *m, String *name, const Value &value); + static void putIndexed(Managed *m, uint index, const Value &value); + static PropertyAttributes query(const Managed *m, String *name); + static PropertyAttributes queryIndexed(const Managed *m, uint index); + static bool deleteProperty(Managed *, String *); + static bool deleteIndexedProperty(Managed *m, uint index); + static bool isEqualTo(Managed *that, Managed *o); + + static const ManagedVTable static_vtbl; +}; + +} + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/jsruntime/qv4stringobject.cpp b/src/qml/jsruntime/qv4stringobject.cpp new file mode 100644 index 0000000000..5afedd3d4f --- /dev/null +++ b/src/qml/jsruntime/qv4stringobject.cpp @@ -0,0 +1,761 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qv4stringobject_p.h" +#include "qv4regexpobject_p.h" +#include "qv4objectproto_p.h" +#include "qv4mm_p.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifndef Q_OS_WIN +# include +# ifndef Q_OS_VXWORKS +# include +# else +# include "qplatformdefs.h" +# endif +#else +# include +#endif + +using namespace QV4; + +DEFINE_MANAGED_VTABLE(StringObject); + +StringObject::StringObject(ExecutionEngine *engine, const Value &value) + : Object(engine), value(value) +{ + vtbl = &static_vtbl; + type = Type_StringObject; + + tmpProperty.value = Value::undefinedValue(); + + assert(value.isString()); + defineReadonlyProperty(engine->id_length, Value::fromUInt32(value.stringValue()->toQString().length())); +} + +Property *StringObject::getIndex(uint index) const +{ + QString str = value.stringValue()->toQString(); + if (index >= (uint)str.length()) + return 0; + String *result = internalClass->engine->newString(str.mid(index, 1)); + tmpProperty.value = Value::fromString(result); + return &tmpProperty; +} + +bool StringObject::deleteIndexedProperty(Managed *m, uint index) +{ + StringObject *o = m->asStringObject(); + if (!o) + m->engine()->current->throwTypeError(); + + if (index < o->value.stringValue()->toQString().length()) { + if (m->engine()->current->strictMode) + m->engine()->current->throwTypeError(); + return false; + } + return true; +} + +Property *StringObject::advanceIterator(Managed *m, ObjectIterator *it, String **name, uint *index, PropertyAttributes *attrs) +{ + StringObject *s = static_cast(m); + uint slen = s->value.stringValue()->toQString().length(); + if (it->arrayIndex < slen) { + while (it->arrayIndex < slen) { + *index = it->arrayIndex; + ++it->arrayIndex; + if (attrs) + *attrs = s->arrayAttributes ? s->arrayAttributes[it->arrayIndex] : PropertyAttributes(Attr_NotWritable|Attr_NotConfigurable); + return s->__getOwnProperty__(*index); + } + it->arrayNode = s->sparseArrayBegin(); + // iterate until we're past the end of the string + while (it->arrayNode && it->arrayNode->key() < slen) + it->arrayNode = it->arrayNode->nextNode(); + } + + return Object::advanceIterator(m, it, name, index, attrs); +} + +void StringObject::markObjects(Managed *that) +{ + StringObject *o = static_cast(that); + o->value.stringValue()->mark(); + Object::markObjects(that); +} + +DEFINE_MANAGED_VTABLE(StringCtor); + +StringCtor::StringCtor(ExecutionContext *scope) + : FunctionObject(scope, scope->engine->newIdentifier(QStringLiteral("String"))) +{ + vtbl = &static_vtbl; +} + +Value StringCtor::construct(Managed *m, Value *argv, int argc) +{ + Value value; + if (argc) + value = Value::fromString(argv[0].toString(m->engine()->current)); + else + value = Value::fromString(m->engine()->current, QString()); + return Value::fromObject(m->engine()->newStringObject(value)); +} + +Value StringCtor::call(Managed *m, const Value &, Value *argv, int argc) +{ + Value value; + if (argc) + value = Value::fromString(argv[0].toString(m->engine()->current)); + else + value = Value::fromString(m->engine()->current, QString()); + return value; +} + +void StringPrototype::init(ExecutionEngine *engine, const Value &ctor) +{ + ctor.objectValue()->defineReadonlyProperty(engine->id_prototype, Value::fromObject(this)); + ctor.objectValue()->defineReadonlyProperty(engine->id_length, Value::fromInt32(1)); + ctor.objectValue()->defineDefaultProperty(engine, QStringLiteral("fromCharCode"), method_fromCharCode, 1); + + defineDefaultProperty(engine, QStringLiteral("constructor"), ctor); + defineDefaultProperty(engine, QStringLiteral("toString"), method_toString); + defineDefaultProperty(engine, QStringLiteral("valueOf"), method_toString); // valueOf and toString are identical + defineDefaultProperty(engine, QStringLiteral("charAt"), method_charAt, 1); + defineDefaultProperty(engine, QStringLiteral("charCodeAt"), method_charCodeAt, 1); + defineDefaultProperty(engine, QStringLiteral("concat"), method_concat, 1); + defineDefaultProperty(engine, QStringLiteral("indexOf"), method_indexOf, 1); + defineDefaultProperty(engine, QStringLiteral("lastIndexOf"), method_lastIndexOf, 1); + defineDefaultProperty(engine, QStringLiteral("localeCompare"), method_localeCompare, 1); + defineDefaultProperty(engine, QStringLiteral("match"), method_match, 1); + defineDefaultProperty(engine, QStringLiteral("replace"), method_replace, 2); + defineDefaultProperty(engine, QStringLiteral("search"), method_search, 1); + defineDefaultProperty(engine, QStringLiteral("slice"), method_slice, 2); + defineDefaultProperty(engine, QStringLiteral("split"), method_split, 2); + defineDefaultProperty(engine, QStringLiteral("substr"), method_substr, 2); + defineDefaultProperty(engine, QStringLiteral("substring"), method_substring, 2); + defineDefaultProperty(engine, QStringLiteral("toLowerCase"), method_toLowerCase); + defineDefaultProperty(engine, QStringLiteral("toLocaleLowerCase"), method_toLocaleLowerCase); + defineDefaultProperty(engine, QStringLiteral("toUpperCase"), method_toUpperCase); + defineDefaultProperty(engine, QStringLiteral("toLocaleUpperCase"), method_toLocaleUpperCase); + defineDefaultProperty(engine, QStringLiteral("trim"), method_trim); +} + +static QString getThisString(ExecutionContext *ctx) +{ + String* str = 0; + Value thisObject = ctx->thisObject; + if (StringObject *thisString = thisObject.asStringObject()) + str = thisString->value.stringValue(); + else if (thisObject.isUndefined() || thisObject.isNull()) + ctx->throwTypeError(); + else + str = ctx->thisObject.toString(ctx); + return str->toQString(); +} + +static QString getThisString(ExecutionContext *context, Value thisObject) +{ + if (thisObject.isString()) + return thisObject.stringValue()->toQString(); + + String* str = 0; + if (StringObject *thisString = thisObject.asStringObject()) + str = thisString->value.stringValue(); + else if (thisObject.isUndefined() || thisObject.isNull()) + context->throwTypeError(); + else + str = thisObject.toString(context); + return str->toQString(); +} + +Value StringPrototype::method_toString(SimpleCallContext *context) +{ + if (context->thisObject.isString()) + return context->thisObject; + + StringObject *o = context->thisObject.asStringObject(); + if (!o) + context->throwTypeError(); + return o->value; +} + +Value StringPrototype::method_charAt(SimpleCallContext *context) +{ + const QString str = getThisString(context, context->thisObject); + + int pos = 0; + if (context->argumentCount > 0) + pos = (int) context->arguments[0].toInteger(); + + QString result; + if (pos >= 0 && pos < str.length()) + result += str.at(pos); + + return Value::fromString(context, result); +} + +Value StringPrototype::method_charCodeAt(SimpleCallContext *context) +{ + const QString str = getThisString(context, context->thisObject); + + int pos = 0; + if (context->argumentCount > 0) + pos = (int) context->arguments[0].toInteger(); + + + if (pos >= 0 && pos < str.length()) + return Value::fromInt32(str.at(pos).unicode()); + + return Value::fromDouble(qSNaN()); +} + +Value StringPrototype::method_concat(SimpleCallContext *context) +{ + QString value = getThisString(context, context->thisObject); + + for (int i = 0; i < context->argumentCount; ++i) { + Value v = __qmljs_to_string(context->arguments[i], context); + assert(v.isString()); + value += v.stringValue()->toQString(); + } + + return Value::fromString(context, value); +} + +Value StringPrototype::method_indexOf(SimpleCallContext *context) +{ + QString value = getThisString(context, context->thisObject); + + QString searchString; + if (context->argumentCount) + searchString = context->arguments[0].toString(context)->toQString(); + + int pos = 0; + if (context->argumentCount > 1) + pos = (int) context->arguments[1].toInteger(); + + int index = -1; + if (! value.isEmpty()) + index = value.indexOf(searchString, qMin(qMax(pos, 0), value.length())); + + return Value::fromDouble(index); +} + +Value StringPrototype::method_lastIndexOf(SimpleCallContext *context) +{ + const QString value = getThisString(context, context->thisObject); + + QString searchString; + if (context->argumentCount) { + Value v = __qmljs_to_string(context->arguments[0], context); + searchString = v.stringValue()->toQString(); + } + + Value posArg = context->argumentCount > 1 ? context->arguments[1] : Value::undefinedValue(); + double position = __qmljs_to_number(posArg); + if (std::isnan(position)) + position = +qInf(); + else + position = trunc(position); + + int pos = trunc(qMin(qMax(position, 0.0), double(value.length()))); + if (!searchString.isEmpty() && pos == value.length()) + --pos; + if (searchString.isNull() && pos == 0) + return Value::fromDouble(-1); + int index = value.lastIndexOf(searchString, pos); + return Value::fromDouble(index); +} + +Value StringPrototype::method_localeCompare(SimpleCallContext *context) +{ + const QString value = getThisString(context, context->thisObject); + const QString that = (context->argumentCount ? context->arguments[0] : Value::undefinedValue()).toString(context)->toQString(); + return Value::fromDouble(QString::localeAwareCompare(value, that)); +} + +Value StringPrototype::method_match(SimpleCallContext *context) +{ + if (context->thisObject.isUndefined() || context->thisObject.isNull()) + context->throwTypeError(); + + String *s = context->thisObject.toString(context); + + Value regexp = context->argumentCount ? context->arguments[0] : Value::undefinedValue(); + RegExpObject *rx = regexp.as(); + if (!rx) + rx = context->engine->regExpCtor.asFunctionObject()->construct(®exp, 1).as(); + + if (!rx) + // ### CHECK + context->throwTypeError(); + + bool global = rx->global; + + // ### use the standard builtin function, not the one that might be redefined in the proto + FunctionObject *exec = context->engine->regExpPrototype->get(context->engine->newString(QStringLiteral("exec")), 0).asFunctionObject(); + + Value arg = Value::fromString(s); + if (!global) + return exec->call(Value::fromObject(rx), &arg, 1); + + String *lastIndex = context->engine->newString(QStringLiteral("lastIndex")); + rx->put(lastIndex, Value::fromInt32(0)); + ArrayObject *a = context->engine->newArrayObject(); + + double previousLastIndex = 0; + uint n = 0; + while (1) { + Value result = exec->call(Value::fromObject(rx), &arg, 1); + if (result.isNull()) + break; + assert(result.isObject()); + double thisIndex = rx->get(lastIndex, 0).toInteger(); + if (previousLastIndex == thisIndex) { + previousLastIndex = thisIndex + 1; + rx->put(lastIndex, Value::fromDouble(previousLastIndex)); + } else { + previousLastIndex = thisIndex; + } + Value matchStr = result.objectValue()->getIndexed(0); + a->arraySet(n, matchStr); + ++n; + } + if (!n) + return Value::nullValue(); + + return Value::fromObject(a); + +} + +static QString makeReplacementString(const QString &input, const QString& replaceValue, uint* matchOffsets, int captureCount) +{ + QString result; + result.reserve(replaceValue.length()); + for (int i = 0; i < replaceValue.length(); ++i) { + if (replaceValue.at(i) == QLatin1Char('$') && i < replaceValue.length() - 1) { + char ch = replaceValue.at(++i).toLatin1(); + uint substStart = JSC::Yarr::offsetNoMatch; + uint substEnd = JSC::Yarr::offsetNoMatch; + if (ch == '$') { + result += ch; + continue; + } else if (ch == '&') { + substStart = matchOffsets[0]; + substEnd = matchOffsets[1]; + } else if (ch == '`') { + substStart = 0; + substEnd = matchOffsets[0]; + } else if (ch == '\'') { + substStart = matchOffsets[1]; + substEnd = input.length(); + } else if (ch >= '1' && ch <= '9') { + char capture = ch - '0'; + if (capture > 0 && capture < captureCount) { + substStart = matchOffsets[capture * 2]; + substEnd = matchOffsets[capture * 2 + 1]; + } + } else if (ch == '0' && i < replaceValue.length() - 1) { + int capture = (ch - '0') * 10; + ch = replaceValue.at(++i).toLatin1(); + capture += ch - '0'; + if (capture > 0 && capture < captureCount) { + substStart = matchOffsets[capture * 2]; + substEnd = matchOffsets[capture * 2 + 1]; + } + } + if (substStart != JSC::Yarr::offsetNoMatch && substEnd != JSC::Yarr::offsetNoMatch) + result += input.midRef(substStart, substEnd - substStart); + } else { + result += replaceValue.at(i); + } + } + return result; +} + +Value StringPrototype::method_replace(SimpleCallContext *ctx) +{ + QString string; + if (StringObject *thisString = ctx->thisObject.asStringObject()) + string = thisString->value.stringValue()->toQString(); + else + string = ctx->thisObject.toString(ctx)->toQString(); + + int numCaptures = 0; + QVarLengthArray matchOffsets; + int numStringMatches = 0; + + Value searchValue = ctx->argument(0); + RegExpObject *regExp = searchValue.as(); + if (regExp) { + uint offset = 0; + while (true) { + int oldSize = matchOffsets.size(); + matchOffsets.resize(matchOffsets.size() + regExp->value->captureCount() * 2); + if (regExp->value->match(string, offset, matchOffsets.data() + oldSize) == JSC::Yarr::offsetNoMatch) { + matchOffsets.resize(oldSize); + break; + } + if (!regExp->global) + break; + offset = qMax(offset + 1, matchOffsets[oldSize + 1]); + } + if (regExp->global) + regExp->lastIndexProperty(ctx)->value = Value::fromUInt32(0); + numStringMatches = matchOffsets.size() / (regExp->value->captureCount() * 2); + numCaptures = regExp->value->captureCount(); + } else { + numCaptures = 1; + QString searchString = searchValue.toString(ctx)->toQString(); + int idx = string.indexOf(searchString); + if (idx != -1) { + numStringMatches = 1; + matchOffsets.resize(2); + matchOffsets[0] = idx; + matchOffsets[1] = idx + searchString.length(); + } + } + + QString result = string; + Value replaceValue = ctx->argument(1); + if (FunctionObject* searchCallback = replaceValue.asFunctionObject()) { + int replacementDelta = 0; + int argc = numCaptures + 2; + Value *args = (Value*)alloca((numCaptures + 2) * sizeof(Value)); + for (int i = 0; i < numStringMatches; ++i) { + for (int k = 0; k < numCaptures; ++k) { + int idx = (i * numCaptures + k) * 2; + uint start = matchOffsets[idx]; + uint end = matchOffsets[idx + 1]; + Value entry = Value::undefinedValue(); + if (start != JSC::Yarr::offsetNoMatch && end != JSC::Yarr::offsetNoMatch) + entry = Value::fromString(ctx, string.mid(start, end - start)); + args[k] = entry; + } + uint matchStart = matchOffsets[i * numCaptures * 2]; + uint matchEnd = matchOffsets[i * numCaptures * 2 + 1]; + args[numCaptures] = Value::fromUInt32(matchStart); + args[numCaptures + 1] = Value::fromString(ctx, string); + Value replacement = searchCallback->call(Value::undefinedValue(), args, argc); + QString replacementString = replacement.toString(ctx)->toQString(); + result.replace(replacementDelta + matchStart, matchEnd - matchStart, replacementString); + replacementDelta += replacementString.length() - matchEnd + matchStart; + } + } else { + QString newString = replaceValue.toString(ctx)->toQString(); + int replacementDelta = 0; + + for (int i = 0; i < numStringMatches; ++i) { + int baseIndex = i * numCaptures * 2; + uint matchStart = matchOffsets[baseIndex]; + uint matchEnd = matchOffsets[baseIndex + 1]; + if (matchStart == JSC::Yarr::offsetNoMatch) + continue; + + QString replacement = makeReplacementString(string, newString, matchOffsets.data() + baseIndex, numCaptures); + result.replace(replacementDelta + matchStart, matchEnd - matchStart, replacement); + replacementDelta += replacement.length() - matchEnd + matchStart; + } + } + + return Value::fromString(ctx, result); +} + +Value StringPrototype::method_search(SimpleCallContext *ctx) +{ + QString string; + if (StringObject *thisString = ctx->thisObject.asStringObject()) + string = thisString->value.stringValue()->toQString(); + else + string = ctx->thisObject.toString(ctx)->toQString(); + + Value regExpValue = ctx->argument(0); + RegExpObject *regExp = regExpValue.as(); + if (!regExp) { + regExpValue = ctx->engine->regExpCtor.asFunctionObject()->construct(®ExpValue, 1); + regExp = regExpValue.as(); + } + uint* matchOffsets = (uint*)alloca(regExp->value->captureCount() * 2 * sizeof(uint)); + uint result = regExp->value->match(string, /*offset*/0, matchOffsets); + if (result == JSC::Yarr::offsetNoMatch) + return Value::fromInt32(-1); + return Value::fromUInt32(result); +} + +Value StringPrototype::method_slice(SimpleCallContext *ctx) +{ + const QString text = getThisString(ctx); + const double length = text.length(); + + double start = ctx->argument(0).toInteger(); + double end = ctx->argument(1).isUndefined() + ? length : ctx->argument(1).toInteger(); + + if (start < 0) + start = qMax(length + start, 0.); + else + start = qMin(start, length); + + if (end < 0) + end = qMax(length + end, 0.); + else + end = qMin(end, length); + + const int intStart = int(start); + const int intEnd = int(end); + + int count = qMax(0, intEnd - intStart); + return Value::fromString(ctx, text.mid(intStart, count)); +} + +Value StringPrototype::method_split(SimpleCallContext *ctx) +{ + QString text; + if (StringObject *thisObject = ctx->thisObject.asStringObject()) + text = thisObject->value.stringValue()->toQString(); + else + text = ctx->thisObject.toString(ctx)->toQString(); + + Value separatorValue = ctx->argumentCount > 0 ? ctx->argument(0) : Value::undefinedValue(); + Value limitValue = ctx->argumentCount > 1 ? ctx->argument(1) : Value::undefinedValue(); + + ArrayObject* array = ctx->engine->newArrayObject(); + Value result = Value::fromObject(array); + + if (separatorValue.isUndefined()) { + if (limitValue.isUndefined()) { + array->push_back(Value::fromString(ctx, text)); + return result; + } + return Value::fromString(ctx, text.left(limitValue.toInteger())); + } + + uint limit = limitValue.isUndefined() ? UINT_MAX : limitValue.toUInt32(); + + if (limit == 0) + return result; + + if (RegExpObject* re = separatorValue.as()) { + if (re->value->pattern().isEmpty()) { + re = 0; + separatorValue = Value::fromString(ctx, QString()); + } + } + + if (RegExpObject* re = separatorValue.as()) { + uint offset = 0; + uint* matchOffsets = (uint*)alloca(re->value->captureCount() * 2 * sizeof(uint)); + while (true) { + uint result = re->value->match(text, offset, matchOffsets); + if (result == JSC::Yarr::offsetNoMatch) + break; + + array->push_back(Value::fromString(ctx, text.mid(offset, matchOffsets[0] - offset))); + offset = qMax(offset + 1, matchOffsets[1]); + + if (array->arrayLength() >= limit) + break; + + for (int i = 1; i < re->value->captureCount(); ++i) { + uint start = matchOffsets[i * 2]; + uint end = matchOffsets[i * 2 + 1]; + array->push_back(Value::fromString(ctx, text.mid(start, end - start))); + if (array->arrayLength() >= limit) + break; + } + } + if (array->arrayLength() < limit) + array->push_back(Value::fromString(ctx, text.mid(offset))); + } else { + QString separator = separatorValue.toString(ctx)->toQString(); + if (separator.isEmpty()) { + for (uint i = 0; i < qMin(limit, uint(text.length())); ++i) + array->push_back(Value::fromString(ctx, text.mid(i, 1))); + return result; + } + + int start = 0; + int end; + while ((end = text.indexOf(separator, start)) != -1) { + array->push_back(Value::fromString(ctx, text.mid(start, end - start))); + start = end + separator.size(); + if (array->arrayLength() >= limit) + break; + } + if (array->arrayLength() < limit && start != -1) + array->push_back(Value::fromString(ctx, text.mid(start))); + } + return result; +} + +Value StringPrototype::method_substr(SimpleCallContext *context) +{ + const QString value = getThisString(context, context->thisObject); + + double start = 0; + if (context->argumentCount > 0) + start = context->arguments[0].toInteger(); + + double length = +qInf(); + if (context->argumentCount > 1) + length = context->arguments[1].toInteger(); + + double count = value.length(); + if (start < 0) + start = qMax(count + start, 0.0); + + length = qMin(qMax(length, 0.0), count - start); + + qint32 x = Value::toInt32(start); + qint32 y = Value::toInt32(length); + return Value::fromString(context, value.mid(x, y)); +} + +Value StringPrototype::method_substring(SimpleCallContext *context) +{ + QString value = getThisString(context, context->thisObject); + int length = value.length(); + + double start = 0; + double end = length; + + if (context->argumentCount > 0) + start = context->arguments[0].toInteger(); + + Value endValue = context->argumentCount > 1 ? context->arguments[1] : Value::undefinedValue(); + if (!endValue.isUndefined()) + end = endValue.toInteger(); + + if (std::isnan(start) || start < 0) + start = 0; + + if (std::isnan(end) || end < 0) + end = 0; + + if (start > length) + start = length; + + if (end > length) + end = length; + + if (start > end) { + double was = start; + start = end; + end = was; + } + + qint32 x = (int)start; + qint32 y = (int)(end - start); + return Value::fromString(context, value.mid(x, y)); +} + +Value StringPrototype::method_toLowerCase(SimpleCallContext *ctx) +{ + QString value = getThisString(ctx); + return Value::fromString(ctx, value.toLower()); +} + +Value StringPrototype::method_toLocaleLowerCase(SimpleCallContext *ctx) +{ + return method_toLowerCase(ctx); +} + +Value StringPrototype::method_toUpperCase(SimpleCallContext *ctx) +{ + QString value = getThisString(ctx); + return Value::fromString(ctx, value.toUpper()); +} + +Value StringPrototype::method_toLocaleUpperCase(SimpleCallContext *ctx) +{ + return method_toUpperCase(ctx); +} + +Value StringPrototype::method_fromCharCode(SimpleCallContext *context) +{ + QString str(context->argumentCount, Qt::Uninitialized); + QChar *ch = str.data(); + for (int i = 0; i < context->argumentCount; ++i) { + *ch = QChar(context->arguments[i].toUInt16()); + ++ch; + } + return Value::fromString(context, str); +} + +Value StringPrototype::method_trim(SimpleCallContext *ctx) +{ + if (ctx->thisObject.isNull() || ctx->thisObject.isUndefined()) + ctx->throwTypeError(); + + QString s = __qmljs_to_string(ctx->thisObject, ctx).stringValue()->toQString(); + const QChar *chars = s.constData(); + int start, end; + for (start = 0; start < s.length(); ++start) { + if (!chars[start].isSpace() && chars[start].unicode() != 0xfeff) + break; + } + for (end = s.length() - 1; end >= start; --end) { + if (!chars[end].isSpace() && chars[end].unicode() != 0xfeff) + break; + } + + return Value::fromString(ctx, QString(chars + start, end - start + 1)); +} diff --git a/src/qml/jsruntime/qv4stringobject_p.h b/src/qml/jsruntime/qv4stringobject_p.h new file mode 100644 index 0000000000..0ef6596235 --- /dev/null +++ b/src/qml/jsruntime/qv4stringobject_p.h @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4STRINGOBJECT_P_H +#define QV4STRINGOBJECT_P_H + +#include "qv4object_p.h" +#include "qv4functionobject_p.h" +#include + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct StringObject: Object { + Q_MANAGED + + Value value; + mutable Property tmpProperty; + StringObject(ExecutionEngine *engine, const Value &value); + + Property *getIndex(uint index) const; + + static bool deleteIndexedProperty(Managed *m, uint index); + +protected: + static Property *advanceIterator(Managed *m, ObjectIterator *it, String **name, uint *index, PropertyAttributes *attrs); + static void markObjects(Managed *that); +}; + +struct StringCtor: FunctionObject +{ + StringCtor(ExecutionContext *scope); + + static Value construct(Managed *m, Value *args, int argc); + static Value call(Managed *that, const Value &, Value *, int); + +protected: + static const ManagedVTable static_vtbl; +}; + +struct StringPrototype: StringObject +{ + StringPrototype(ExecutionEngine *engine): StringObject(engine, Value::fromString(engine, QString())) {} + void init(ExecutionEngine *engine, const Value &ctor); + + static Value method_toString(SimpleCallContext *context); + static Value method_charAt(SimpleCallContext *context); + static Value method_charCodeAt(SimpleCallContext *context); + static Value method_concat(SimpleCallContext *context); + static Value method_indexOf(SimpleCallContext *context); + static Value method_lastIndexOf(SimpleCallContext *context); + static Value method_localeCompare(SimpleCallContext *context); + static Value method_match(SimpleCallContext *context); + static Value method_replace(SimpleCallContext *ctx); + static Value method_search(SimpleCallContext *ctx); + static Value method_slice(SimpleCallContext *ctx); + static Value method_split(SimpleCallContext *ctx); + static Value method_substr(SimpleCallContext *context); + static Value method_substring(SimpleCallContext *context); + static Value method_toLowerCase(SimpleCallContext *ctx); + static Value method_toLocaleLowerCase(SimpleCallContext *ctx); + static Value method_toUpperCase(SimpleCallContext *ctx); + static Value method_toLocaleUpperCase(SimpleCallContext *ctx); + static Value method_fromCharCode(SimpleCallContext *context); + static Value method_trim(SimpleCallContext *ctx); +}; + +} + +QT_END_NAMESPACE + +#endif // QV4ECMAOBJECTS_P_H diff --git a/src/qml/jsruntime/qv4unwindhelper.cpp b/src/qml/jsruntime/qv4unwindhelper.cpp new file mode 100644 index 0000000000..beb5132626 --- /dev/null +++ b/src/qml/jsruntime/qv4unwindhelper.cpp @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include + +#if CPU(X86_64) && (OS(LINUX) || OS(MAC_OS_X)) +# define USE_DW2_HELPER +#elif CPU(X86) && COMPILER(GCC) +# define USE_DW2_HELPER +#elif CPU(ARM) && (OS(LINUX) || OS(QNX)) +# define USE_ARM_HELPER +#elif OS(WINDOWS) + // SJLJ will unwind on Windows +# define USE_NULL_HELPER +#elif OS(IOS) + // SJLJ will unwind on iOS +# define USE_NULL_HELPER +#else +# warning "Unsupported/untested platform!" +# define USE_NULL_HELPER +#endif + +#ifdef USE_DW2_HELPER +# include +#endif // USE_DW2_HELPER + +#ifdef USE_ARM_HELPER +# include +#endif // USE_ARM_HELPER + +QT_BEGIN_NAMESPACE + +#ifdef USE_NULL_HELPER +using namespace QV4; +void UnwindHelper::prepareForUnwind(ExecutionContext *) {} +void UnwindHelper::registerFunction(Function *function) {Q_UNUSED(function);} +void UnwindHelper::registerFunctions(const QVector &functions) {Q_UNUSED(functions);} +void UnwindHelper::deregisterFunction(Function *function) {Q_UNUSED(function);} +void UnwindHelper::deregisterFunctions(const QVector &functions) {Q_UNUSED(functions);} +#endif // USE_NULL_HELPER + +QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4unwindhelper_p-arm.h b/src/qml/jsruntime/qv4unwindhelper_p-arm.h new file mode 100644 index 0000000000..dd1f1e4856 --- /dev/null +++ b/src/qml/jsruntime/qv4unwindhelper_p-arm.h @@ -0,0 +1,233 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4UNWINDHELPER_PDW2_H +#define QV4UNWINDHELPER_PDW2_H + +#include "qv4unwindhelper_p.h" +#include "qv4functionobject_p.h" +#include "qv4function_p.h" +#include + +#include +#include + +#define __USE_GNU +#include + +#if USE(LIBUNWIND_DEBUG) +#include +#include +#endif + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +static void *removeThumbBit(void *addr) +{ + return reinterpret_cast(reinterpret_cast(addr) & ~1u); +} + +static QMutex functionProtector; +static QMap allFunctions; + +static Function *lookupFunction(void *pc) +{ + quintptr key = reinterpret_cast(pc); + QMap::ConstIterator it = allFunctions.lowerBound(key); + if (it != allFunctions.begin() && allFunctions.count() > 0) + --it; + if (it == allFunctions.end()) + return 0; + + quintptr codeStart = reinterpret_cast(removeThumbBit((*it)->codeRef.code().executableAddress())); + if (key < codeStart || key >= codeStart + (*it)->codeSize) + return 0; + return *it; +} + + +/* Program: +vsp = r4 (REG_TO_SP r4) +vsp -= 8 * 4 -- > vsp = vsp - (7 << 2) - 4 +pop r12, r10, r9, r8, r7, r6, r5, r4 +pop r4 +pop lr +pop r0, r1, r2, r3 +*/ + +#define REG_TO_SP 0b10010000 +#define VSP_MINUS 0b01000000 +#define POP_REG_MULTI 0b10000000 +#define POP_R4_MULTI 0b10100000 +#define POP_R4_R14_MULTI 0b10101000 +#define POP_R0_TO_R3 0b10110001 +#define FINISH 0b10110000 + +#define MK_UW_WORD(first, second, third, fourth) \ + (((first) << 24) | \ + ((second) << 16) | \ + ((third) << 8) | \ + (fourth)) + +static unsigned int extbl[] = { + MK_UW_WORD(0x80 | // High bit set to indicate that this isn't a PREL31 + 2, // Choose personality routine #2 + 2, // Number of 4 byte words used to encode remaining unwind instructions + REG_TO_SP | 4, // Encoded program from above. + VSP_MINUS | 7), + MK_UW_WORD(POP_REG_MULTI | 1, 0b01111111, + POP_R4_R14_MULTI, + POP_R0_TO_R3), + MK_UW_WORD(0b00001111, + FINISH, + FINISH, + FINISH) +}; + +static unsigned write_prel31(unsigned *addr, void *ptr) +{ + int delta = (char *)ptr - (char*)addr; + if (delta < 0) + delta |= (1 << 30); + else + delta &= ~(1 << 30); + *addr = ((unsigned)delta) & 0x7fffffffU; +} + +void UnwindHelper::deregisterFunction(Function *function) +{ + QMutexLocker locker(&functionProtector); + allFunctions.remove(reinterpret_cast(function->code)); +} + +void UnwindHelper::deregisterFunctions(const QVector &functions) +{ + QMutexLocker locker(&functionProtector); + foreach (Function *f, functions) + allFunctions.remove(reinterpret_cast(f->code)); +} + +void UnwindHelper::registerFunction(Function *function) +{ + QMutexLocker locker(&functionProtector); + allFunctions.insert(reinterpret_cast(function->code), function); +} + +void UnwindHelper::registerFunctions(const QVector &functions) +{ + QMutexLocker locker(&functionProtector); + foreach (Function *f, functions) + allFunctions.insert(reinterpret_cast(f->code), f); +} + +void UnwindHelper::prepareForUnwind(ExecutionContext *) +{ +} + +int UnwindHelper::unwindInfoSize() +{ + return 2 * sizeof(unsigned int) // 2 extbl entries + + sizeof(extbl); +} + +void UnwindHelper::writeARMUnwindInfo(void *codeAddr, int codeSize) +{ + unsigned int *exidx = (unsigned int *)((char *)codeAddr + codeSize); + + unsigned char *exprog = (unsigned char *)((unsigned char *)codeAddr + codeSize + 8); + + write_prel31(exidx, codeAddr); + exidx[1] = 4; // PREL31 offset to extbl, which follows right afterwards + + memcpy(exprog, extbl, sizeof(extbl)); + +#if USE(LIBUNWIND_DEBUG) + unw_dyn_info_t *info = (unw_dyn_info_t*)malloc(sizeof(unw_dyn_info_t)); + info->start_ip = (unw_word_t)codeAddr; + info->end_ip = info->start_ip + codeSize; + info->gp = 0; + info->format = UNW_INFO_FORMAT_ARM_EXIDX; + info->u.rti.name_ptr = 0; + info->u.rti.segbase = 0; + info->u.rti.table_len = 8; + info->u.rti.table_data = (unw_word_t)exidx; + _U_dyn_register(info); +#endif +} + +} + +QT_END_NAMESPACE + +#if defined(Q_OS_ANDROID) +extern "C" void *dl_unwind_find_exidx(void *pc, int *entryCount); +#endif + +extern "C" Q_DECL_EXPORT void *__gnu_Unwind_Find_exidx(void *pc, int *entryCount) +{ +#if !defined(Q_OS_ANDROID) + typedef void *(*Old_Unwind_Find_exidx)(void*, int*); + static Old_Unwind_Find_exidx oldFunction = 0; + if (!oldFunction) + oldFunction = (Old_Unwind_Find_exidx)dlsym(RTLD_NEXT, "__gnu_Unwind_Find_exidx"); +#endif + + { + QMutexLocker locker(&QT_PREPEND_NAMESPACE(QV4::functionProtector)); + QV4::Function *function = QT_PREPEND_NAMESPACE(QV4::lookupFunction(pc)); + if (function) { + *entryCount = 1; + void * codeStart = QT_PREPEND_NAMESPACE(QV4::removeThumbBit(function->codeRef.code().executableAddress())); + // At the end of the function we store our synthetic exception table entry. + return (char *)codeStart + function->codeSize; + } + } + +#if defined(Q_OS_ANDROID) + return dl_unwind_find_exidx(pc, entryCount); +#else + return oldFunction(pc, entryCount); +#endif +} + +#endif // QV4UNWINDHELPER_PDW2_H diff --git a/src/qml/jsruntime/qv4unwindhelper_p-dw2.h b/src/qml/jsruntime/qv4unwindhelper_p-dw2.h new file mode 100644 index 0000000000..57615f0999 --- /dev/null +++ b/src/qml/jsruntime/qv4unwindhelper_p-dw2.h @@ -0,0 +1,191 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4UNWINDHELPER_PDW2_H +#define QV4UNWINDHELPER_PDW2_H + +#include "qv4unwindhelper_p.h" +#include "qv4functionobject_p.h" +#include "qv4function_p.h" +#include +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +namespace { +#if CPU(X86_64) +// Generated by fdegen +static const unsigned char cie_fde_data[] = { + 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x8, 0x78, 0x10, 0xc, 0x7, 0x8, + 0x90, 0x1, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, + 0x18, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x41, 0x13, 0x7e, 0x86, + 0x2, 0x43, 0xd, 0x6, 0x8c, 0x3, 0x8e, 0x4, + 0x0, 0x0, 0x0, 0x0 +}; +static const int fde_offset = 20; +static const int initial_location_offset = 28; +static const int address_range_offset = 36; +#elif CPU(X86) +static const unsigned char cie_fde_data[] = { + 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x4, 0x7c, 0x8, 0xc, 0x4, 0x4, + 0x88, 0x1, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, + 0x18, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x41, 0x13, 0x7e, 0x85, + 0x2, 0x43, 0xd, 0x5, 0x86, 0x3, 0x87, 0x4, + 0x0, 0x0, 0x0, 0x0, +}; +static const int fde_offset = 20; +static const int initial_location_offset = 28; +static const int address_range_offset = 32; +#endif + +void writeIntPtrValue(unsigned char *addr, intptr_t val) +{ + addr[0] = (val >> 0) & 0xff; + addr[1] = (val >> 8) & 0xff; + addr[2] = (val >> 16) & 0xff; + addr[3] = (val >> 24) & 0xff; +#if QT_POINTER_SIZE == 8 + addr[4] = (val >> 32) & 0xff; + addr[5] = (val >> 40) & 0xff; + addr[6] = (val >> 48) & 0xff; + addr[7] = (val >> 56) & 0xff; +#endif +} +} // anonymous namespace + +extern "C" void __register_frame(void *fde); +extern "C" void __deregister_frame(void *fde); + +struct UnwindInfo : public ExecutableAllocator::PlatformUnwindInfo +{ + UnwindInfo(const QByteArray &cieFde); + virtual ~UnwindInfo(); + QByteArray data; +}; + +UnwindInfo::UnwindInfo(const QByteArray &cieFde) + : data(cieFde) +{ + __register_frame(data.data() + fde_offset); +} + +UnwindInfo::~UnwindInfo() +{ + __deregister_frame(data.data() + fde_offset); +} + +static void ensureUnwindInfo(Function *f) +{ + if (!f->codeRef) + return; // Not a JIT generated function + + JSC::ExecutableMemoryHandle *handle = f->codeRef.executableMemory(); + if (!handle) + return; + ExecutableAllocator::ChunkOfPages *chunk = handle->chunk(); + + // Already registered? + if (chunk->unwindInfo) + return; + + QByteArray info; + info.resize(sizeof(cie_fde_data)); + + unsigned char *cie_and_fde = reinterpret_cast(info.data()); + memcpy(cie_and_fde, cie_fde_data, sizeof(cie_fde_data)); + + intptr_t ptr = static_cast(chunk->pages->base()) - static_cast(0); + writeIntPtrValue(cie_and_fde + initial_location_offset, ptr); + + writeIntPtrValue(cie_and_fde + address_range_offset, chunk->pages->size()); + + chunk->unwindInfo = new UnwindInfo(info); +} + +void UnwindHelper::prepareForUnwind(ExecutionContext *context) +{ + for (ExecutionContext *ctx = context; ctx; ctx = ctx->parent) { + if (CallContext *callCtx = ctx->asCallContext()) + if (FunctionObject *fobj = callCtx->function) + if (Function *fun = fobj->function) + ensureUnwindInfo(fun); + for (ExecutionContext::EvalCode *code = ctx->currentEvalCode; + code; code = code->next) + ensureUnwindInfo(code->function); + } + + if (context->engine->globalCode) + ensureUnwindInfo(context->engine->globalCode); +} + +void UnwindHelper::registerFunction(Function *) +{ +} + +void UnwindHelper::registerFunctions(const QVector&) +{ +} + +void UnwindHelper::deregisterFunction(Function *) +{ +} + +void UnwindHelper::deregisterFunctions(const QVector &) +{ +} + +} + +QT_END_NAMESPACE + +#endif // QV4UNWINDHELPER_PDW2_H diff --git a/src/qml/jsruntime/qv4unwindhelper_p.h b/src/qml/jsruntime/qv4unwindhelper_p.h new file mode 100644 index 0000000000..9ef564449a --- /dev/null +++ b/src/qml/jsruntime/qv4unwindhelper_p.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4UNWINDHELPER_H +#define QV4UNWINDHELPER_H + +#include + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct Function; +struct ExecutionContext; + +class UnwindHelper +{ +public: + static void prepareForUnwind(ExecutionContext *ctx); + static void registerFunction(Function *function); + static void registerFunctions(const QVector &functions); + static void deregisterFunction(Function *function); + static void deregisterFunctions(const QVector &functions); +#ifdef Q_PROCESSOR_ARM + static int unwindInfoSize(); + static void writeARMUnwindInfo(void *codeAddr, int codeSize); +#endif +}; + +} + +QT_END_NAMESPACE + +#endif // QV4UNWINDHELPER_H diff --git a/src/qml/jsruntime/qv4util_p.h b/src/qml/jsruntime/qv4util_p.h new file mode 100644 index 0000000000..dbd9f89faa --- /dev/null +++ b/src/qml/jsruntime/qv4util_p.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4UTIL_H +#define QV4UTIL_H + +#include "qv4global_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +template +struct TemporaryAssignment +{ + TemporaryAssignment(T &var, const T& temporaryValue) + : variable(var) + , savedValue(var) + { + variable = temporaryValue; + } + ~TemporaryAssignment() + { + variable = savedValue; + } + T &variable; + T savedValue; +private: + TemporaryAssignment(const TemporaryAssignment&); + TemporaryAssignment operator=(const TemporaryAssignment&); +}; + +} + +QT_END_NAMESPACE + +#endif // QV4UTIL_H diff --git a/src/qml/jsruntime/qv4value.cpp b/src/qml/jsruntime/qv4value.cpp new file mode 100644 index 0000000000..a41262f12f --- /dev/null +++ b/src/qml/jsruntime/qv4value.cpp @@ -0,0 +1,403 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include +#include +#include +#include "qv4mm_p.h" +#include "qv4exception_p.h" + +#include + +using namespace QV4; + +int Value::toUInt16() const +{ + if (isConvertibleToInt()) + return (ushort)(uint)integerValue(); + + double number = __qmljs_to_number(*this); + + double D16 = 65536.0; + if ((number >= 0 && number < D16)) + return static_cast(number); + + if (!std::isfinite(number)) + return +0; + + double d = ::floor(::fabs(number)); + if (std::signbit(number)) + d = -d; + + number = ::fmod(d , D16); + + if (number < 0) + number += D16; + + return (unsigned short)number; +} + +double Value::toInteger() const +{ + if (isConvertibleToInt()) + return int_32; + + return Value::toInteger(__qmljs_to_number(*this)); +} + +double Value::toNumber() const +{ + return __qmljs_to_number(*this); +} + +QString Value::toQString() const +{ + switch (type()) { + case Value::Undefined_Type: + return QStringLiteral("undefined"); + case Value::Null_Type: + return QStringLiteral("null"); + case Value::Boolean_Type: + if (booleanValue()) + return QStringLiteral("true"); + else + return QStringLiteral("false"); + case Value::String_Type: + return stringValue()->toQString(); + case Value::Object_Type: { + ExecutionContext *ctx = objectValue()->internalClass->engine->current; + try { + Value prim = __qmljs_to_primitive(*this, STRING_HINT); + if (prim.isPrimitive()) + return prim.toQString(); + } catch (Exception &e) { + e.accept(ctx); + try { + Value prim = __qmljs_to_primitive(e.value(), STRING_HINT); + if (prim.isPrimitive()) + return prim.toQString(); + } catch(Exception &e) { + e.accept(ctx); + } + } + return QString(); + } + case Value::Integer_Type: { + QString str; + __qmljs_numberToString(&str, (double)int_32, 10); + return str; + } + default: { // double + QString str; + __qmljs_numberToString(&str, doubleValue(), 10); + return str; + } + } // switch +} + +bool Value::sameValue(Value other) const { + if (val == other.val) + return true; + if (isString() && other.isString()) + return stringValue()->isEqualTo(other.stringValue()); + if (isInteger()) + return int_32 ? (double(int_32) == other.dbl) : (other.val == 0); + if (other.isInteger()) + return other.int_32 ? (dbl == double(other.int_32)) : (val == 0); + return false; +} + +Value Value::fromString(ExecutionContext *ctx, const QString &s) +{ + return fromString(ctx->engine->newString(s)); +} + +Value Value::fromString(ExecutionEngine *engine, const QString &s) +{ + return fromString(engine->newString(s)); +} + + +int Value::toInt32(double number) +{ + const double D32 = 4294967296.0; + const double D31 = D32 / 2.0; + + if ((number >= -D31 && number < D31)) + return static_cast(number); + + + if (!std::isfinite(number)) + return 0; + + double d = ::floor(::fabs(number)); + if (std::signbit(number)) + d = -d; + + number = ::fmod(d , D32); + + if (number < -D31) + number += D32; + else if (number >= D31) + number -= D32; + + return int(number); +} + +unsigned int Value::toUInt32(double number) +{ + const double D32 = 4294967296.0; + if ((number >= 0 && number < D32)) + return static_cast(number); + + if (!std::isfinite(number)) + return +0; + + double d = ::floor(::fabs(number)); + if (std::signbit(number)) + d = -d; + + number = ::fmod(d , D32); + + if (number < 0) + number += D32; + + return unsigned(number); +} + +double Value::toInteger(double number) +{ + if (std::isnan(number)) + return +0; + else if (! number || std::isinf(number)) + return number; + const double v = floor(fabs(number)); + return std::signbit(number) ? -v : v; +} + +String *Value::toString(ExecutionContext *ctx) const +{ + if (isString()) + return stringValue(); + return __qmljs_convert_to_string(ctx, *this); +} + +Value Value::property(ExecutionContext *ctx, String *name) const +{ + return isObject() ? objectValue()->get(name) : undefinedValue(); +} + + +PersistentValue::PersistentValue(const Value &val) + : d(new PersistentValuePrivate(val)) +{ +} + +PersistentValue::PersistentValue(const PersistentValue &other) + : d(other.d) +{ + if (d) + d->ref(); +} + +PersistentValue &PersistentValue::operator=(const PersistentValue &other) +{ + if (d == other.d) + return *this; + + // the memory manager cleans up those with a refcount of 0 + + if (d) + d->deref(); + d = other.d; + if (d) + d->ref(); + + return *this; +} + +PersistentValue &PersistentValue::operator =(const Value &other) +{ + if (!d) { + d = new PersistentValuePrivate(other); + return *this; + } + d = d->detach(other); + return *this; +} + +PersistentValue::~PersistentValue() +{ + if (d) + d->deref(); +} + +WeakValue::WeakValue(const Value &val) + : d(new PersistentValuePrivate(val, /*engine*/0, /*weak*/true)) +{ +} + +WeakValue::WeakValue(const WeakValue &other) + : d(other.d) +{ + if (d) + d->ref(); +} + +WeakValue &WeakValue::operator=(const WeakValue &other) +{ + if (d == other.d) + return *this; + + // the memory manager cleans up those with a refcount of 0 + + if (d) + d->deref(); + d = other.d; + if (d) + d->ref(); + + return *this; +} + +WeakValue &WeakValue::operator =(const Value &other) +{ + if (!d) { + d = new PersistentValuePrivate(other, /*engine*/0, /*weak*/true); + return *this; + } + d = d->detach(other, /*weak*/true); + return *this; +} + + +WeakValue::~WeakValue() +{ + if (d) + d->deref(); +} + +void WeakValue::markOnce() +{ + if (!d) + return; + Managed *m = d->value.asManaged(); + if (!m) + return; + m->mark(); +} + +PersistentValuePrivate::PersistentValuePrivate(const Value &v, ExecutionEngine *e, bool weak) + : value(v) + , refcount(1) + , prev(0) + , next(0) + , engine(e) +{ + if (!engine) { + Managed *m = v.asManaged(); + if (!m) + return; + + engine = m->engine(); + } + if (engine) { + PersistentValuePrivate **listRoot = weak ? &engine->memoryManager->m_weakValues : &engine->memoryManager->m_persistentValues; + + prev = listRoot; + next = *listRoot; + *prev = this; + if (next) + next->prev = &this->next; + } +} + +PersistentValuePrivate::~PersistentValuePrivate() +{ +} + +void PersistentValuePrivate::removeFromList() +{ + if (prev) { + if (next) + next->prev = prev; + *prev = next; + next = 0; + prev = 0; + } +} + +void PersistentValuePrivate::deref() +{ + // if engine is not 0, they are registered with the memory manager + // and will get cleaned up in the next gc run + if (!--refcount) { + removeFromList(); + delete this; + } +} + +PersistentValuePrivate *PersistentValuePrivate::detach(const QV4::Value &value, bool weak) +{ + if (refcount == 1) { + this->value = value; + + Managed *m = value.asManaged(); + if (!prev) { + if (m) { + ExecutionEngine *engine = m->engine(); + if (engine) { + PersistentValuePrivate **listRoot = weak ? &engine->memoryManager->m_weakValues : &engine->memoryManager->m_persistentValues; + prev = listRoot; + next = *listRoot; + *prev = this; + if (next) + next->prev = &this->next; + } + } + } else if (!m) + removeFromList(); + + return this; + } + --refcount; + return new PersistentValuePrivate(value, engine, weak); +} + diff --git a/src/qml/jsruntime/qv4value_def_p.h b/src/qml/jsruntime/qv4value_def_p.h new file mode 100644 index 0000000000..a44af16b6a --- /dev/null +++ b/src/qml/jsruntime/qv4value_def_p.h @@ -0,0 +1,282 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4VALUE_DEF_P_H +#define QV4VALUE_DEF_P_H + +#include +#include "qv4global_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +typedef uint Bool; + +struct Q_QML_EXPORT Value +{ + union { + quint64 val; + double dbl; + struct { +#if Q_BYTE_ORDER != Q_LITTLE_ENDIAN + uint tag; +#endif + union { + uint uint_32; + int int_32; +#if QT_POINTER_SIZE == 4 + Managed *m; + Object *o; + String *s; +#endif + }; +#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN + uint tag; +#endif + }; + }; + + enum Masks { + NaN_Mask = 0x7ff80000, + NotDouble_Mask = 0x7ffc0000, + Type_Mask = 0xffff8000, + Immediate_Mask = NotDouble_Mask | 0x00008000, + IsManaged_Mask = Type_Mask & ~0x10000, + Tag_Shift = 32 + }; + enum ValueType { + Undefined_Type = Immediate_Mask | 0x00000, + Null_Type = Immediate_Mask | 0x10000, + Boolean_Type = Immediate_Mask | 0x20000, + Integer_Type = Immediate_Mask | 0x30000, + Object_Type = NotDouble_Mask | 0x00000, + String_Type = NotDouble_Mask | 0x10000, + Deleted_Type = NotDouble_Mask | 0x30000 + }; + + enum ImmediateFlags { + ConvertibleToInt = Immediate_Mask | 0x1 + }; + + enum ValueTypeInternal { + _Undefined_Type = Undefined_Type, + _Empty_Type = Deleted_Type, + _Null_Type = Null_Type | ConvertibleToInt, + _Boolean_Type = Boolean_Type | ConvertibleToInt, + _Integer_Type = Integer_Type | ConvertibleToInt, + _Object_Type = Object_Type, + _String_Type = String_Type + + }; + + inline unsigned type() const { + return tag & Type_Mask; + } + + // used internally in property + inline bool isEmpty() const { return tag == _Empty_Type; } + + inline bool isUndefined() const { return tag == _Undefined_Type; } + inline bool isNull() const { return tag == _Null_Type; } + inline bool isBoolean() const { return tag == _Boolean_Type; } + inline bool isInteger() const { return tag == _Integer_Type; } + inline bool isDouble() const { return (tag & NotDouble_Mask) != NotDouble_Mask; } + inline bool isNumber() const { return tag == _Integer_Type || (tag & NotDouble_Mask) != NotDouble_Mask; } +#if QT_POINTER_SIZE == 8 + inline bool isString() const { return (tag & Type_Mask) == String_Type; } + inline bool isObject() const { return (tag & Type_Mask) == Object_Type; } +#else + inline bool isString() const { return tag == String_Type; } + inline bool isObject() const { return tag == Object_Type; } +#endif + inline bool isManaged() const { return (tag & IsManaged_Mask) == Object_Type; } + inline bool isConvertibleToInt() const { return (tag & ConvertibleToInt) == ConvertibleToInt; } + inline bool isInt32() { + if (tag == _Integer_Type) + return true; + if (isDouble()) { + int i = (int)dbl; + if (i == dbl) { + int_32 = i; + tag = _Integer_Type; + return true; + } + } + return false; + } + + bool booleanValue() const { + return int_32; + } + double doubleValue() const { + return dbl; + } + void setDouble(double d) { + dbl = d; + } + double asDouble() const { + if (tag == _Integer_Type) + return int_32; + return dbl; + } + int integerValue() const { + return int_32; + } + +#if QT_POINTER_SIZE == 8 + String *stringValue() const { + return (String *)(val & ~(quint64(Type_Mask) << Tag_Shift)); + } + Object *objectValue() const { + return (Object *)(val & ~(quint64(Type_Mask) << Tag_Shift)); + } + Managed *managed() const { + return (Managed *)(val & ~(quint64(Type_Mask) << Tag_Shift)); + } +#else + String *stringValue() const { + return s; + } + Object *objectValue() const { + return o; + } + Managed *managed() const { + return m; + } +#endif + + quint64 rawValue() const { + return val; + } + + static Value emptyValue(); + static Value undefinedValue(); + static Value nullValue(); + static Value fromBoolean(Bool b); + static Value fromDouble(double d); + static Value fromInt32(int i); + static Value fromUInt32(uint i); + static Value fromString(String *s); + static Value fromObject(Object *o); + +#ifndef QMLJS_LLVM_RUNTIME + static Value fromString(ExecutionContext *ctx, const QString &fromString); + static Value fromString(ExecutionEngine *engine, const QString &s); +#endif + + static double toInteger(double fromNumber); + static int toInt32(double value); + static unsigned int toUInt32(double value); + + int toUInt16() const; + int toInt32() const; + unsigned int toUInt32() const; + + bool toBoolean() const; + double toInteger() const; + double toNumber() const; + QString toQString() const; + String *toString(ExecutionContext *ctx) const; + Object *toObject(ExecutionContext *ctx) const; + + inline bool isPrimitive() const { return !isObject(); } +#if QT_POINTER_SIZE == 8 + inline bool integerCompatible() const { + const quint64 mask = quint64(ConvertibleToInt) << 32; + return (val & mask) == mask; + } + static inline bool integerCompatible(Value a, Value b) { + const quint64 mask = quint64(ConvertibleToInt) << 32; + return ((a.val & b.val) & mask) == mask; + } + static inline bool bothDouble(Value a, Value b) { + const quint64 mask = quint64(NotDouble_Mask) << 32; + return ((a.val | b.val) & mask) != mask; + } +#else + inline bool integerCompatible() const { + return (tag & ConvertibleToInt) == ConvertibleToInt; + } + static inline bool integerCompatible(Value a, Value b) { + return ((a.tag & b.tag) & ConvertibleToInt) == ConvertibleToInt; + } + static inline bool bothDouble(Value a, Value b) { + return ((a.tag | b.tag) & NotDouble_Mask) != NotDouble_Mask; + } +#endif + inline bool tryIntegerConversion() { + bool b = isConvertibleToInt(); + if (b) + tag = _Integer_Type; + return b; + } + + String *asString() const; + Managed *asManaged() const; + Object *asObject() const; + FunctionObject *asFunctionObject() const; + BooleanObject *asBooleanObject() const; + NumberObject *asNumberObject() const; + StringObject *asStringObject() const; + DateObject *asDateObject() const; + ArrayObject *asArrayObject() const; + ErrorObject *asErrorObject() const; + + template inline T *as() const; + + uint asArrayIndex() const; + uint asArrayLength(bool *ok) const; + + Value property(ExecutionContext *ctx, String *name) const; + + inline ExecutionEngine *engine() const; + + // Section 9.12 + bool sameValue(Value other) const; + + inline void mark() const; +}; + +} + +QT_END_NAMESPACE + +#endif // QV4VALUE_DEF_P_H diff --git a/src/qml/jsruntime/qv4value_p.h b/src/qml/jsruntime/qv4value_p.h new file mode 100644 index 0000000000..2a783ed34b --- /dev/null +++ b/src/qml/jsruntime/qv4value_p.h @@ -0,0 +1,426 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QMLJS_VALUE_H +#define QMLJS_VALUE_H + +#include // this HAS to come + +#include +#include +#include "qv4global_p.h" +#include "qv4string_p.h" +#include +#include "qv4managed_p.h" + +//#include + +#include "qv4value_def_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +double __qmljs_to_number(const QV4::Value &value); +Q_QML_EXPORT QV4::String *__qmljs_convert_to_string(QV4::ExecutionContext *ctx, const QV4::Value &value); +QV4::Object *__qmljs_convert_to_object(QV4::ExecutionContext *ctx, const QV4::Value &value); + + +inline ExecutionEngine *Value::engine() const { + Managed *m = asManaged(); + return m ? m->engine() : 0; +} + +inline void Value::mark() const { + Managed *m = asManaged(); + if (m) + m->mark(); +} + +inline Value Value::undefinedValue() +{ + Value v; +#if QT_POINTER_SIZE == 8 + v.val = quint64(_Undefined_Type) << Tag_Shift; +#else + v.tag = _Undefined_Type; + v.int_32 = 0; +#endif + return v; +} + +inline Value Value::nullValue() +{ + Value v; +#if QT_POINTER_SIZE == 8 + v.val = quint64(_Null_Type) << Tag_Shift; +#else + v.tag = _Null_Type; + v.int_32 = 0; +#endif + return v; +} + +inline Value Value::emptyValue() +{ + Value v; + v.tag = Value::_Empty_Type; + v.uint_32 = 0; + return v; +} + + +inline Value Value::fromBoolean(Bool b) +{ + Value v; + v.tag = _Boolean_Type; + v.int_32 = (bool)b; + return v; +} + +inline Value Value::fromDouble(double d) +{ + Value v; + v.dbl = d; + return v; +} + +inline Value Value::fromInt32(int i) +{ + Value v; + v.tag = _Integer_Type; + v.int_32 = i; + return v; +} + +inline Value Value::fromUInt32(uint i) +{ + Value v; + if (i < INT_MAX) { + v.tag = _Integer_Type; + v.int_32 = (int)i; + } else { + v.dbl = i; + } + return v; +} + +inline Value Value::fromString(String *s) +{ + Value v; +#if QT_POINTER_SIZE == 8 + v.val = (quint64)s; + v.val |= quint64(_String_Type) << Tag_Shift; +#else + v.tag = _String_Type; + v.s = s; +#endif + return v; +} + +inline Value Value::fromObject(Object *o) +{ + Value v; +#if QT_POINTER_SIZE == 8 + v.val = (quint64)o; + v.val |= quint64(_Object_Type) << Tag_Shift; +#else + v.tag = _Object_Type; + v.o = o; +#endif + return v; +} + +inline bool Value::toBoolean() const +{ + switch (type()) { + case Value::Undefined_Type: + case Value::Null_Type: + return false; + case Value::Boolean_Type: + case Value::Integer_Type: + return (bool)int_32; + case Value::String_Type: + return stringValue()->toQString().length() > 0; + case Value::Object_Type: + return true; + default: // double + if (! doubleValue() || std::isnan(doubleValue())) + return false; + return true; + } +} + +inline Object *Value::toObject(ExecutionContext *ctx) const +{ + if (isObject()) + return objectValue(); + return __qmljs_convert_to_object(ctx, *this); +} + +inline int Value::toInt32() const +{ + if (isConvertibleToInt()) + return int_32; + double d; + if (isDouble()) + d = dbl; + else + d = __qmljs_to_number(*this); + + const double D32 = 4294967296.0; + const double D31 = D32 / 2.0; + + if ((d >= -D31 && d < D31)) + return static_cast(d); + + return Value::toInt32(__qmljs_to_number(*this)); +} + +inline unsigned int Value::toUInt32() const +{ + if (isConvertibleToInt()) + return (unsigned) int_32; + double d; + if (isDouble()) + d = dbl; + else + d = __qmljs_to_number(*this); + + const double D32 = 4294967296.0; + if (dbl >= 0 && dbl < D32) + return static_cast(dbl); + return toUInt32(d); +} + +inline uint Value::asArrayIndex() const +{ + if (isInteger() && int_32 >= 0) + return (uint)int_32; + if (!isDouble()) + return UINT_MAX; + uint idx = (uint)dbl; + if (idx != dbl) + return UINT_MAX; + return idx; +} + +inline uint Value::asArrayLength(bool *ok) const +{ + *ok = true; + if (isConvertibleToInt() && int_32 >= 0) + return (uint)int_32; + if (isDouble()) { + uint idx = (uint)dbl; + if ((double)idx != dbl) { + *ok = false; + return UINT_MAX; + } + return idx; + } + if (isString()) + return stringValue()->toUInt(ok); + + uint idx = toUInt32(); + double d = toNumber(); + if (d != idx) { + *ok = false; + return UINT_MAX; + } + return idx; +} + +inline String *Value::asString() const +{ + if (isString()) + return stringValue(); + return 0; +} + +inline Managed *Value::asManaged() const +{ + if (isManaged()) + return managed(); + return 0; +} + +inline Object *Value::asObject() const +{ + return isObject() ? objectValue() : 0; +} + +inline FunctionObject *Value::asFunctionObject() const +{ + return isObject() ? managed()->asFunctionObject() : 0; +} + +inline BooleanObject *Value::asBooleanObject() const +{ + return isObject() ? managed()->asBooleanObject() : 0; +} + +inline NumberObject *Value::asNumberObject() const +{ + return isObject() ? managed()->asNumberObject() : 0; +} + +inline StringObject *Value::asStringObject() const +{ + return isObject() ? managed()->asStringObject() : 0; +} + +inline DateObject *Value::asDateObject() const +{ + return isObject() ? managed()->asDateObject() : 0; +} + +inline ArrayObject *Value::asArrayObject() const +{ + return isObject() ? managed()->asArrayObject() : 0; +} + +inline ErrorObject *Value::asErrorObject() const +{ + return isObject() ? managed()->asErrorObject() : 0; +} + +// ### +inline Value Managed::construct(Value *args, int argc) { + return vtbl->construct(this, args, argc); +} +inline Value Managed::call(const Value &thisObject, Value *args, int argc) { + return vtbl->call(this, thisObject, args, argc); +} + +struct PersistentValuePrivate +{ + PersistentValuePrivate(const Value &v, ExecutionEngine *engine = 0, bool weak = false); + virtual ~PersistentValuePrivate(); + Value value; + uint refcount; + QV4::ExecutionEngine *engine; + PersistentValuePrivate **prev; + PersistentValuePrivate *next; + + void removeFromList(); + void ref() { ++refcount; } + void deref(); + PersistentValuePrivate *detach(const QV4::Value &value, bool weak = false); + + bool checkEngine(QV4::ExecutionEngine *otherEngine) { + if (!engine) { + Q_ASSERT(!value.isObject()); + engine = otherEngine; + } + return (engine == otherEngine); + } +}; + +class Q_QML_EXPORT PersistentValue +{ +public: + PersistentValue() : d(0) {} + PersistentValue(const Value &val); + PersistentValue(const PersistentValue &other); + PersistentValue &operator=(const PersistentValue &other); + PersistentValue &operator=(const Value &other); + ~PersistentValue(); + + Value value() const { + return d ? d->value : Value::emptyValue(); + } + + ExecutionEngine *engine() { + if (!d) + return 0; + Managed *m = d->value.asManaged(); + return m ? m->engine() : 0; + } + + operator Value() const { return value(); } + + bool isEmpty() const { return !d || d->value.isEmpty(); } + void clear() { + *this = PersistentValue(); + } + +private: + PersistentValuePrivate *d; +}; + +class Q_QML_EXPORT WeakValue +{ +public: + WeakValue() : d(0) {} + WeakValue(const Value &val); + WeakValue(const WeakValue &other); + WeakValue &operator=(const WeakValue &other); + WeakValue &operator=(const Value &other); + ~WeakValue(); + + Value value() const { + return d ? d->value : Value::emptyValue(); + } + + ExecutionEngine *engine() { + if (!d) + return 0; + Managed *m = d->value.asManaged(); + return m ? m->engine() : 0; + } + + operator Value() const { return value(); } + + bool isEmpty() const { return !d || d->value.isEmpty(); } + void clear() { + *this = WeakValue(); + } + + void markOnce(); + +private: + PersistentValuePrivate *d; +}; + +} // namespace QV4 + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/jsruntime/qv4variantobject.cpp b/src/qml/jsruntime/qv4variantobject.cpp new file mode 100644 index 0000000000..f18c5b582e --- /dev/null +++ b/src/qml/jsruntime/qv4variantobject.cpp @@ -0,0 +1,203 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4variantobject_p.h" +#include "qv4functionobject_p.h" +#include "qv4objectproto_p.h" +#include +#include + +QT_BEGIN_NAMESPACE + +using namespace QV4; + +DEFINE_MANAGED_VTABLE(VariantObject); + +VariantObject::VariantObject(ExecutionEngine *engine, const QVariant &value) + : Object(engine) + , ExecutionEngine::ScarceResourceData(value) + , m_vmePropertyReferenceCount(0) +{ + vtbl = &static_vtbl; + prototype = engine->variantPrototype; + if (isScarce()) + internalClass->engine->scarceResources.insert(this); +} + +QVariant VariantObject::toVariant(const QV4::Value &v) +{ + if (Object *o = v.asObject()) + return o->engine()->v8Engine->variantFromJS(v); + + if (v.isString()) + return QVariant(v.stringValue()->toQString()); + if (v.isBoolean()) + return QVariant(v.booleanValue()); + if (v.isNumber()) { + QV4::Value val = v; + if (val.isInt32()) + return QVariant(val.integerValue()); + return QVariant(v.asDouble()); + } + if (v.isNull()) + return QVariant(QMetaType::VoidStar, 0); + assert (v.isUndefined() || v.isEmpty()); + return QVariant(); +} + +bool VariantObject::isScarce() const +{ + QVariant::Type t = data.type(); + return t == QVariant::Pixmap || t == QVariant::Image; +} + +void VariantObject::destroy(Managed *that) +{ + VariantObject *v = static_cast(that); + if (v->isScarce()) + v->node.remove(); + v->~VariantObject(); +} + +bool VariantObject::isEqualTo(Managed *m, Managed *other) +{ + QV4::VariantObject *lv = m->as(); + assert(lv); + + if (QV4::VariantObject *rv = other->as()) + return lv->data == rv->data; + + if (QV4::QmlValueTypeWrapper *v = other->as()) + return v->isEqual(lv->data); + + return false; +} + +void VariantObject::addVmePropertyReference() +{ + if (isScarce() && ++m_vmePropertyReferenceCount == 1) { + // remove from the ep->scarceResources list + // since it is now no longer eligible to be + // released automatically by the engine. + node.remove(); + } +} + +void VariantObject::removeVmePropertyReference() +{ + if (isScarce() && --m_vmePropertyReferenceCount == 0) { + // and add to the ep->scarceResources list + // since it is now eligible to be released + // automatically by the engine. + internalClass->engine->scarceResources.insert(this); + } +} + + +VariantPrototype::VariantPrototype(ExecutionEngine *engine) + : VariantObject(engine, QVariant()) +{ + prototype = engine->objectPrototype; +} + +void VariantPrototype::init(ExecutionEngine *engine) +{ + defineDefaultProperty(engine, QStringLiteral("preserve"), method_preserve, 0); + defineDefaultProperty(engine, QStringLiteral("destroy"), method_destroy, 0); + defineDefaultProperty(engine, QStringLiteral("valueOf"), method_valueOf, 0); + defineDefaultProperty(engine, QStringLiteral("toString"), method_toString, 0); +} + +QV4::Value VariantPrototype::method_preserve(SimpleCallContext *ctx) +{ + VariantObject *o = ctx->thisObject.as(); + if (o && o->isScarce()) + o->node.remove(); + return Value::undefinedValue(); +} + +QV4::Value VariantPrototype::method_destroy(SimpleCallContext *ctx) +{ + VariantObject *o = ctx->thisObject.as(); + if (o) { + if (o->isScarce()) + o->node.remove(); + o->data = QVariant(); + } + return QV4::Value::undefinedValue(); +} + +QV4::Value VariantPrototype::method_toString(SimpleCallContext *ctx) +{ + VariantObject *o = ctx->thisObject.as(); + if (!o) + return Value::undefinedValue(); + QString result = o->data.toString(); + if (result.isEmpty() && !o->data.canConvert(QVariant::String)) + result = QString::fromLatin1("QVariant(%0)").arg(QString::fromLatin1(o->data.typeName())); + return Value::fromString(ctx->engine->newString(result)); +} + +QV4::Value VariantPrototype::method_valueOf(SimpleCallContext *ctx) +{ + VariantObject *o = ctx->thisObject.as(); + if (o) { + QVariant v = o->data; + switch (v.type()) { + case QVariant::Invalid: + return Value::undefinedValue(); + case QVariant::String: + return Value::fromString(ctx->engine->newString(v.toString())); + case QVariant::Int: + return Value::fromInt32(v.toInt()); + case QVariant::Double: + case QVariant::UInt: + return Value::fromDouble(v.toDouble()); + case QVariant::Bool: + return Value::fromBoolean(v.toBool()); + default: + break; + } + } + return ctx->thisObject; +} + +QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4variantobject_p.h b/src/qml/jsruntime/qv4variantobject_p.h new file mode 100644 index 0000000000..876539aae1 --- /dev/null +++ b/src/qml/jsruntime/qv4variantobject_p.h @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4VARIANTOBJECT_P_H +#define QV4VARIANTOBJECT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct VariantObject : Object, public ExecutionEngine::ScarceResourceData +{ + Q_MANAGED +public: + VariantObject(ExecutionEngine *engine, const QVariant &value); + + static QVariant toVariant(const QV4::Value &v); + + void addVmePropertyReference(); + void removeVmePropertyReference(); + bool isScarce() const; + int m_vmePropertyReferenceCount; + + static void destroy(Managed *that); + static bool isEqualTo(Managed *m, Managed *other); +}; + +struct VariantPrototype : VariantObject +{ +public: + VariantPrototype(ExecutionEngine *engine); + + void init(ExecutionEngine *engine); + + static Value method_preserve(SimpleCallContext *ctx); + static Value method_destroy(SimpleCallContext *ctx); + static Value method_toString(SimpleCallContext *ctx); + static Value method_valueOf(SimpleCallContext *ctx); +}; + +} + +QT_END_NAMESPACE + +#endif + diff --git a/src/qml/qml.pro b/src/qml/qml.pro index adbb2c7639..c046c6105a 100644 --- a/src/qml/qml.pro +++ b/src/qml/qml.pro @@ -21,6 +21,8 @@ HEADERS += qtqmlglobal.h \ #modules include(util/util.pri) +include(compiler/compiler.pri) +include(jsruntime/jsruntime.pri) include(qml/qml.pri) include(debugger/debugger.pri) include(animations/animations.pri) diff --git a/src/qml/qml/qml.pri b/src/qml/qml/qml.pri index 3a18d02871..1914a388d0 100644 --- a/src/qml/qml/qml.pri +++ b/src/qml/qml/qml.pri @@ -136,4 +136,3 @@ include(parser/parser.pri) include(rewriter/rewriter.pri) include(ftw/ftw.pri) include(v8/v8.pri) -include(v4/v4.pri) diff --git a/src/qml/qml/v4/moth/moth.pri b/src/qml/qml/v4/moth/moth.pri deleted file mode 100644 index 73bd893286..0000000000 --- a/src/qml/qml/v4/moth/moth.pri +++ /dev/null @@ -1,13 +0,0 @@ -INCLUDEPATH += $$PWD - -HEADERS += \ - $$PWD/qv4isel_moth_p.h \ - $$PWD/qv4instr_moth_p.h \ - $$PWD/qv4vme_moth_p.h - -SOURCES += \ - $$PWD/qv4isel_moth.cpp \ - $$PWD/qv4instr_moth.cpp \ - $$PWD/qv4vme_moth.cpp - -#DEFINES += DO_TRACE_INSTR diff --git a/src/qml/qml/v4/moth/qv4instr_moth.cpp b/src/qml/qml/v4/moth/qv4instr_moth.cpp deleted file mode 100644 index ec68ede72d..0000000000 --- a/src/qml/qml/v4/moth/qv4instr_moth.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4instr_moth_p.h" - -using namespace QQmlJS; -using namespace QQmlJS::Moth; - -int Instr::size(Type type) -{ -#define MOTH_RETURN_INSTR_SIZE(I, FMT) case I: return InstrMeta<(int)I>::Size; - switch (type) { - FOR_EACH_MOTH_INSTR(MOTH_RETURN_INSTR_SIZE) - default: return 0; - } -#undef MOTH_RETURN_INSTR_SIZE -} - diff --git a/src/qml/qml/v4/moth/qv4instr_moth_p.h b/src/qml/qml/v4/moth/qv4instr_moth_p.h deleted file mode 100644 index ac7196e2d1..0000000000 --- a/src/qml/qml/v4/moth/qv4instr_moth_p.h +++ /dev/null @@ -1,621 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QV4INSTR_MOTH_P_H -#define QV4INSTR_MOTH_P_H - -#include -#include - -QT_BEGIN_NAMESPACE - -#define FOR_EACH_MOTH_INSTR(F) \ - F(Ret, ret) \ - F(LoadValue, loadValue) \ - F(LoadClosure, loadClosure) \ - F(MoveTemp, moveTemp) \ - F(LoadName, loadName) \ - F(StoreName, storeName) \ - F(LoadElement, loadElement) \ - F(StoreElement, storeElement) \ - F(LoadProperty, loadProperty) \ - F(StoreProperty, storeProperty) \ - F(Push, push) \ - F(EnterTry, enterTry) \ - F(CallValue, callValue) \ - F(CallProperty, callProperty) \ - F(CallElement, callElement) \ - F(CallActivationProperty, callActivationProperty) \ - F(CallBuiltinThrow, callBuiltinThrow) \ - F(CallBuiltinFinishTry, callBuiltinFinishTry) \ - F(CallBuiltinPushScope, callBuiltinPushScope) \ - F(CallBuiltinPopScope, callBuiltinPopScope) \ - F(CallBuiltinForeachIteratorObject, callBuiltinForeachIteratorObject) \ - F(CallBuiltinForeachNextPropertyName, callBuiltinForeachNextPropertyName) \ - F(CallBuiltinDeleteMember, callBuiltinDeleteMember) \ - F(CallBuiltinDeleteSubscript, callBuiltinDeleteSubscript) \ - F(CallBuiltinDeleteName, callBuiltinDeleteName) \ - F(CallBuiltinTypeofMember, callBuiltinTypeofMember) \ - F(CallBuiltinTypeofSubscript, callBuiltinTypeofSubscript) \ - F(CallBuiltinTypeofName, callBuiltinTypeofName) \ - F(CallBuiltinTypeofValue, callBuiltinTypeofValue) \ - F(CallBuiltinPostIncMember, callBuiltinPostIncMember) \ - F(CallBuiltinPostIncSubscript, callBuiltinPostIncSubscript) \ - F(CallBuiltinPostIncName, callBuiltinPostIncName) \ - F(CallBuiltinPostIncValue, callBuiltinPostIncValue) \ - F(CallBuiltinPostDecMember, callBuiltinPostDecMember) \ - F(CallBuiltinPostDecSubscript, callBuiltinPostDecSubscript) \ - F(CallBuiltinPostDecName, callBuiltinPostDecName) \ - F(CallBuiltinPostDecValue, callBuiltinPostDecValue) \ - F(CallBuiltinDeclareVar, callBuiltinDeclareVar) \ - F(CallBuiltinDefineGetterSetter, callBuiltinDefineGetterSetter) \ - F(CallBuiltinDefineProperty, callBuiltinDefineProperty) \ - F(CallBuiltinDefineArray, callBuiltinDefineArray) \ - F(CallBuiltinDefineObjectLiteral, callBuiltinDefineObjectLiteral) \ - F(CreateValue, createValue) \ - F(CreateProperty, createProperty) \ - F(CreateActivationProperty, createActivationProperty) \ - F(Jump, jump) \ - F(CJump, cjump) \ - F(Unop, unop) \ - F(Binop, binop) \ - F(BinopContext, binopContext) \ - F(AddNumberParams, addNumberParams) \ - F(MulNumberParams, mulNumberParams) \ - F(SubNumberParams, subNumberParams) \ - F(LoadThis, loadThis) \ - F(InplaceElementOp, inplaceElementOp) \ - F(InplaceMemberOp, inplaceMemberOp) \ - F(InplaceNameOp, inplaceNameOp) - -#if defined(Q_CC_GNU) && (!defined(Q_CC_INTEL) || __INTEL_COMPILER >= 1200) -# define MOTH_THREADED_INTERPRETER -#endif - -#define MOTH_INSTR_ALIGN_MASK (Q_ALIGNOF(QQmlJS::Moth::Instr) - 1) - -#ifdef MOTH_THREADED_INTERPRETER -# define MOTH_INSTR_HEADER void *code; \ - unsigned int breakPoint : 1; -#else -# define MOTH_INSTR_HEADER quint8 instructionType; \ - unsigned int breakPoint : 1; -#endif - -#define MOTH_INSTR_ENUM(I, FMT) I, -#define MOTH_INSTR_SIZE(I, FMT) ((sizeof(QQmlJS::Moth::Instr::instr_##FMT) + MOTH_INSTR_ALIGN_MASK) & ~MOTH_INSTR_ALIGN_MASK) - - -namespace QQmlJS { -namespace Moth { - -union Instr -{ - struct Param { - enum { - ValueType = 0, - ArgumentType = 1, - LocalType = 2, - TempType = 3, - ScopedLocalType = 4 - }; - QV4::Value value; - unsigned type : 3; - unsigned scope : 29; - unsigned index; - - bool isValue() const { return type == ValueType; } - bool isArgument() const { return type == ArgumentType; } - bool isLocal() const { return type == LocalType; } - bool isTemp() const { return type == TempType; } - bool isScopedLocal() const { return type == ScopedLocalType; } - - static Param createValue(const QV4::Value &v) - { - Param p; - p.type = ValueType; - p.scope = 0; - p.value = v; - return p; - } - - static Param createArgument(unsigned idx, uint scope) - { - Param p; - p.type = ArgumentType; - p.scope = scope; - p.index = idx; - return p; - } - - static Param createLocal(unsigned idx) - { - Param p; - p.type = LocalType; - p.scope = 0; - p.index = idx; - return p; - } - - static Param createTemp(unsigned idx) - { - Param p; - p.type = TempType; - p.scope = 0; - p.index = idx; - return p; - } - - static Param createScopedLocal(unsigned idx, uint scope) - { - Param p; - p.type = ScopedLocalType; - p.scope = scope; - p.index = idx; - return p; - } - - inline bool operator==(const Param &other) const - { return type == other.type && scope == other.scope && index == other.index; } - - inline bool operator!=(const Param &other) const - { return !(*this == other); } - }; - - enum Type { - FOR_EACH_MOTH_INSTR(MOTH_INSTR_ENUM) - }; - - struct instr_common { - MOTH_INSTR_HEADER - }; - struct instr_ret { - MOTH_INSTR_HEADER - Param result; - }; - struct instr_loadValue { - MOTH_INSTR_HEADER - Param value; - Param result; - }; - struct instr_moveTemp { - MOTH_INSTR_HEADER - Param source; - Param result; - }; - struct instr_loadClosure { - MOTH_INSTR_HEADER - QV4::Function *value; - Param result; - }; - struct instr_loadName { - MOTH_INSTR_HEADER - QV4::String *name; - Param result; - }; - struct instr_storeName { - MOTH_INSTR_HEADER - QV4::String *name; - Param source; - }; - struct instr_loadProperty { - MOTH_INSTR_HEADER - QV4::String *name; - Param base; - Param result; - }; - struct instr_storeProperty { - MOTH_INSTR_HEADER - QV4::String *name; - Param base; - Param source; - }; - struct instr_loadElement { - MOTH_INSTR_HEADER - Param base; - Param index; - Param result; - }; - struct instr_storeElement { - MOTH_INSTR_HEADER - Param base; - Param index; - Param source; - }; - struct instr_push { - MOTH_INSTR_HEADER - quint32 value; - }; - struct instr_enterTry { - MOTH_INSTR_HEADER - ptrdiff_t tryOffset; - ptrdiff_t catchOffset; - QV4::String *exceptionVarName; - Param exceptionVar; - }; - struct instr_callValue { - MOTH_INSTR_HEADER - quint32 argc; - quint32 args; - Param dest; - Param result; - }; - struct instr_callProperty { - MOTH_INSTR_HEADER - QV4::String *name; - quint32 argc; - quint32 args; - Param base; - Param result; - }; - struct instr_callElement { - MOTH_INSTR_HEADER - Param base; - Param index; - quint32 argc; - quint32 args; - Param result; - }; - struct instr_callActivationProperty { - MOTH_INSTR_HEADER - QV4::String *name; - quint32 argc; - quint32 args; - Param result; - }; - struct instr_callBuiltinThrow { - MOTH_INSTR_HEADER - Param arg; - }; - struct instr_callBuiltinFinishTry { - MOTH_INSTR_HEADER - }; - struct instr_callBuiltinPushScope { - MOTH_INSTR_HEADER - Param arg; - }; - struct instr_callBuiltinPopScope { - MOTH_INSTR_HEADER - }; - struct instr_callBuiltinForeachIteratorObject { - MOTH_INSTR_HEADER - Param arg; - Param result; - }; - struct instr_callBuiltinForeachNextPropertyName { - MOTH_INSTR_HEADER - Param arg; - Param result; - }; - struct instr_callBuiltinDeleteMember { - MOTH_INSTR_HEADER - QV4::String *member; - Param base; - Param result; - }; - struct instr_callBuiltinDeleteSubscript { - MOTH_INSTR_HEADER - Param base; - Param index; - Param result; - }; - struct instr_callBuiltinDeleteName { - MOTH_INSTR_HEADER - QV4::String *name; - Param result; - }; - struct instr_callBuiltinTypeofMember { - MOTH_INSTR_HEADER - QV4::String *member; - Param base; - Param result; - }; - struct instr_callBuiltinTypeofSubscript { - MOTH_INSTR_HEADER - Param base; - Param index; - Param result; - }; - struct instr_callBuiltinTypeofName { - MOTH_INSTR_HEADER - QV4::String *name; - Param result; - }; - struct instr_callBuiltinTypeofValue { - MOTH_INSTR_HEADER - Param value; - Param result; - }; - struct instr_callBuiltinPostIncMember { - MOTH_INSTR_HEADER - Param base; - QV4::String *member; - Param result; - }; - struct instr_callBuiltinPostIncSubscript { - MOTH_INSTR_HEADER - Param base; - Param index; - Param result; - }; - struct instr_callBuiltinPostIncName { - MOTH_INSTR_HEADER - QV4::String *name; - Param result; - }; - struct instr_callBuiltinPostIncValue { - MOTH_INSTR_HEADER - Param value; - Param result; - }; - struct instr_callBuiltinPostDecMember { - MOTH_INSTR_HEADER - Param base; - QV4::String *member; - Param result; - }; - struct instr_callBuiltinPostDecSubscript { - MOTH_INSTR_HEADER - Param base; - Param index; - Param result; - }; - struct instr_callBuiltinPostDecName { - MOTH_INSTR_HEADER - QV4::String *name; - Param result; - }; - struct instr_callBuiltinPostDecValue { - MOTH_INSTR_HEADER - Param value; - Param result; - }; - struct instr_callBuiltinDeclareVar { - MOTH_INSTR_HEADER - QV4::String *varName; - bool isDeletable; - }; - struct instr_callBuiltinDefineGetterSetter { - MOTH_INSTR_HEADER - QV4::String *name; - Param object; - Param getter; - Param setter; - }; - struct instr_callBuiltinDefineProperty { - MOTH_INSTR_HEADER - QV4::String *name; - Param object; - Param value; - }; - struct instr_callBuiltinDefineArray { - MOTH_INSTR_HEADER - quint32 argc; - quint32 args; - Param result; - }; - struct instr_callBuiltinDefineObjectLiteral { - MOTH_INSTR_HEADER - QV4::InternalClass *internalClass; - quint32 args; - Param result; - }; - struct instr_createValue { - MOTH_INSTR_HEADER - quint32 argc; - quint32 args; - Param func; - Param result; - }; - struct instr_createProperty { - MOTH_INSTR_HEADER - QV4::String *name; - quint32 argc; - quint32 args; - Param base; - Param result; - }; - struct instr_createActivationProperty { - MOTH_INSTR_HEADER - QV4::String *name; - quint32 argc; - quint32 args; - Param result; - }; - struct instr_jump { - MOTH_INSTR_HEADER - ptrdiff_t offset; - }; - struct instr_cjump { - MOTH_INSTR_HEADER - ptrdiff_t offset; - Param condition; - }; - struct instr_unop { - MOTH_INSTR_HEADER - QV4::UnaryOpName alu; - Param source; - Param result; - }; - struct instr_binop { - MOTH_INSTR_HEADER - QV4::BinOp alu; - Param lhs; - Param rhs; - Param result; - }; - struct instr_binopContext { - MOTH_INSTR_HEADER - QV4::BinOpContext alu; - Param lhs; - Param rhs; - Param result; - }; - struct instr_addNumberParams { - MOTH_INSTR_HEADER - Param lhs; - Param rhs; - Param result; - }; - struct instr_mulNumberParams { - MOTH_INSTR_HEADER - Param lhs; - Param rhs; - Param result; - }; - struct instr_subNumberParams { - MOTH_INSTR_HEADER - Param lhs; - Param rhs; - Param result; - }; - struct instr_loadThis { - MOTH_INSTR_HEADER - Param result; - }; - struct instr_inplaceElementOp { - MOTH_INSTR_HEADER - QV4::InplaceBinOpElement alu; - Param base; - Param index; - Param source; - }; - struct instr_inplaceMemberOp { - MOTH_INSTR_HEADER - QV4::InplaceBinOpMember alu; - QV4::String *member; - Param base; - Param source; - }; - struct instr_inplaceNameOp { - MOTH_INSTR_HEADER - QV4::InplaceBinOpName alu; - QV4::String *name; - Param source; - }; - - instr_common common; - instr_ret ret; - instr_loadValue loadValue; - instr_moveTemp moveTemp; - instr_loadClosure loadClosure; - instr_loadName loadName; - instr_storeName storeName; - instr_loadElement loadElement; - instr_storeElement storeElement; - instr_loadProperty loadProperty; - instr_storeProperty storeProperty; - instr_push push; - instr_enterTry enterTry; - instr_callValue callValue; - instr_callProperty callProperty; - instr_callElement callElement; - instr_callActivationProperty callActivationProperty; - instr_callBuiltinThrow callBuiltinThrow; - instr_callBuiltinFinishTry callBuiltinFinishTry; - instr_callBuiltinPushScope callBuiltinPushScope; - instr_callBuiltinPopScope callBuiltinPopScope; - instr_callBuiltinForeachIteratorObject callBuiltinForeachIteratorObject; - instr_callBuiltinForeachNextPropertyName callBuiltinForeachNextPropertyName; - instr_callBuiltinDeleteMember callBuiltinDeleteMember; - instr_callBuiltinDeleteSubscript callBuiltinDeleteSubscript; - instr_callBuiltinDeleteName callBuiltinDeleteName; - instr_callBuiltinTypeofMember callBuiltinTypeofMember; - instr_callBuiltinTypeofSubscript callBuiltinTypeofSubscript; - instr_callBuiltinTypeofName callBuiltinTypeofName; - instr_callBuiltinTypeofValue callBuiltinTypeofValue; - instr_callBuiltinPostIncMember callBuiltinPostIncMember; - instr_callBuiltinPostIncSubscript callBuiltinPostIncSubscript; - instr_callBuiltinPostIncName callBuiltinPostIncName; - instr_callBuiltinPostIncValue callBuiltinPostIncValue; - instr_callBuiltinPostDecMember callBuiltinPostDecMember; - instr_callBuiltinPostDecSubscript callBuiltinPostDecSubscript; - instr_callBuiltinPostDecName callBuiltinPostDecName; - instr_callBuiltinPostDecValue callBuiltinPostDecValue; - instr_callBuiltinDeclareVar callBuiltinDeclareVar; - instr_callBuiltinDefineGetterSetter callBuiltinDefineGetterSetter; - instr_callBuiltinDefineProperty callBuiltinDefineProperty; - instr_callBuiltinDefineArray callBuiltinDefineArray; - instr_callBuiltinDefineObjectLiteral callBuiltinDefineObjectLiteral; - instr_createValue createValue; - instr_createProperty createProperty; - instr_createActivationProperty createActivationProperty; - instr_jump jump; - instr_cjump cjump; - instr_unop unop; - instr_binop binop; - instr_binopContext binopContext; - instr_addNumberParams addNumberParams; - instr_mulNumberParams mulNumberParams; - instr_subNumberParams subNumberParams; - instr_loadThis loadThis; - instr_inplaceElementOp inplaceElementOp; - instr_inplaceMemberOp inplaceMemberOp; - instr_inplaceNameOp inplaceNameOp; - - static int size(Type type); -}; - -template -struct InstrMeta { -}; - -#define MOTH_INSTR_META_TEMPLATE(I, FMT) \ - template<> struct InstrMeta<(int)Instr::I> { \ - enum { Size = MOTH_INSTR_SIZE(I, FMT) }; \ - typedef Instr::instr_##FMT DataType; \ - static const DataType &data(const Instr &instr) { return instr.FMT; } \ - static void setData(Instr &instr, const DataType &v) { instr.FMT = v; } \ - }; -FOR_EACH_MOTH_INSTR(MOTH_INSTR_META_TEMPLATE); -#undef MOTH_INSTR_META_TEMPLATE - -template -class InstrData : public InstrMeta::DataType -{ -}; - -} // namespace Moth -} // namespace QQmlJS - -QT_END_NAMESPACE - -#endif // QV4INSTR_MOTH_P_H diff --git a/src/qml/qml/v4/moth/qv4isel_moth.cpp b/src/qml/qml/v4/moth/qv4isel_moth.cpp deleted file mode 100644 index 610f429f5d..0000000000 --- a/src/qml/qml/v4/moth/qv4isel_moth.cpp +++ /dev/null @@ -1,1089 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4isel_util_p.h" -#include "qv4isel_moth_p.h" -#include "qv4vme_moth_p.h" -#include "qv4ssa_p.h" -#include -#include -#include -#include - -#undef USE_TYPE_INFO - -using namespace QQmlJS; -using namespace QQmlJS::Moth; - -namespace { - -inline QV4::BinOp aluOpFunction(V4IR::AluOp op) -{ - switch (op) { - case V4IR::OpInvalid: - return 0; - case V4IR::OpIfTrue: - return 0; - case V4IR::OpNot: - return 0; - case V4IR::OpUMinus: - return 0; - case V4IR::OpUPlus: - return 0; - case V4IR::OpCompl: - return 0; - case V4IR::OpBitAnd: - return QV4::__qmljs_bit_and; - case V4IR::OpBitOr: - return QV4::__qmljs_bit_or; - case V4IR::OpBitXor: - return QV4::__qmljs_bit_xor; - case V4IR::OpAdd: - return 0; - case V4IR::OpSub: - return QV4::__qmljs_sub; - case V4IR::OpMul: - return QV4::__qmljs_mul; - case V4IR::OpDiv: - return QV4::__qmljs_div; - case V4IR::OpMod: - return QV4::__qmljs_mod; - case V4IR::OpLShift: - return QV4::__qmljs_shl; - case V4IR::OpRShift: - return QV4::__qmljs_shr; - case V4IR::OpURShift: - return QV4::__qmljs_ushr; - case V4IR::OpGt: - return QV4::__qmljs_gt; - case V4IR::OpLt: - return QV4::__qmljs_lt; - case V4IR::OpGe: - return QV4::__qmljs_ge; - case V4IR::OpLe: - return QV4::__qmljs_le; - case V4IR::OpEqual: - return QV4::__qmljs_eq; - case V4IR::OpNotEqual: - return QV4::__qmljs_ne; - case V4IR::OpStrictEqual: - return QV4::__qmljs_se; - case V4IR::OpStrictNotEqual: - return QV4::__qmljs_sne; - case V4IR::OpInstanceof: - return 0; - case V4IR::OpIn: - return 0; - case V4IR::OpAnd: - return 0; - case V4IR::OpOr: - return 0; - default: - assert(!"Unknown AluOp"); - return 0; - } -}; -} // anonymous namespace - -// TODO: extend to optimize out temp-to-temp moves, where the lifetime of one temp ends at that statement. -// To handle that, add a hint when such a move will occur, and add a stmt for the hint. -// Then when asked for a register, check if the active statement is the terminating statement, and if so, apply the hint. -// This generalises the hint usage for Phi removal too, when the phi is passed in there as the current statement. -class QQmlJS::Moth::StackSlotAllocator -{ - QHash _slotForTemp; - QHash _hints; - QVector _activeSlots; - - QHash _intervals; - -public: - StackSlotAllocator(const QList &ranges, int maxTempCount) - : _activeSlots(maxTempCount) - { - _intervals.reserve(ranges.size()); - foreach (const V4IR::LifeTimeInterval &r, ranges) - _intervals[r.temp()] = r; - } - - void addHint(const V4IR::Temp &hintedSlotOfTemp, const V4IR::Temp &newTemp) - { - if (hintedSlotOfTemp.kind != V4IR::Temp::VirtualRegister - || newTemp.kind != V4IR::Temp::VirtualRegister) - return; - - if (_slotForTemp.contains(newTemp) || _hints.contains(newTemp)) - return; - - int hintedSlot = _slotForTemp.value(hintedSlotOfTemp, -1); - Q_ASSERT(hintedSlot >= 0); - _hints[newTemp] = hintedSlot; - } - - int stackSlotFor(V4IR::Temp *t, V4IR::Stmt *currentStmt) { - int idx = _slotForTemp.value(*t, -1); - if (idx == -1) - idx = allocateSlot(t, currentStmt); - Q_ASSERT(idx >= 0); - return idx; - } - -private: - int allocateSlot(V4IR::Temp *t, V4IR::Stmt *currentStmt) { - const V4IR::LifeTimeInterval &interval = _intervals[*t]; - int idx = _hints.value(*t, -1); - if (idx != -1 && _activeSlots[idx] <= currentStmt->id) { - _slotForTemp[*t] = idx; - _activeSlots[idx] = interval.end(); - return idx; - } - - for (int i = 0, ei = _activeSlots.size(); i != ei; ++i) { - if (_activeSlots[i] < currentStmt->id) { - _slotForTemp[*t] = i; - _activeSlots[i] = interval.end(); - return i; - } - } - - return -1; - } -}; - -InstructionSelection::InstructionSelection(QV4::ExecutionEngine *engine, V4IR::Module *module) - : EvalInstructionSelection(engine, module) - , _function(0) - , _vmFunction(0) - , _block(0) - , _codeStart(0) - , _codeNext(0) - , _codeEnd(0) - , _stackSlotAllocator(0) - , _currentStatement(0) -{ -} - -InstructionSelection::~InstructionSelection() -{ -} - -void InstructionSelection::run(QV4::Function *vmFunction, V4IR::Function *function) -{ - V4IR::BasicBlock *block = 0, *nextBlock = 0; - - QHash > patches; - QHash addrs; - - int codeSize = 4096; - uchar *codeStart = new uchar[codeSize]; - memset(codeStart, 0, codeSize); - uchar *codeNext = codeStart; - uchar *codeEnd = codeStart + codeSize; - - qSwap(_function, function); - qSwap(_vmFunction, vmFunction); - qSwap(block, _block); - qSwap(nextBlock, _nextBlock); - qSwap(patches, _patches); - qSwap(addrs, _addrs); - qSwap(codeStart, _codeStart); - qSwap(codeNext, _codeNext); - qSwap(codeEnd, _codeEnd); - - V4IR::Optimizer opt(_function); - opt.run(); - StackSlotAllocator *stackSlotAllocator = 0; - if (opt.isInSSA()) - stackSlotAllocator = new StackSlotAllocator(opt.lifeRanges(), _function->tempCount); - qSwap(_stackSlotAllocator, stackSlotAllocator); - V4IR::Stmt *cs = 0; - qSwap(_currentStatement, cs); - - int locals = frameSize(); - assert(locals >= 0); - - Instruction::Push push; - push.value = quint32(locals); - addInstruction(push); - - for (int i = 0, ei = _function->basicBlocks.size(); i != ei; ++i) { - _block = _function->basicBlocks[i]; - _nextBlock = (i < ei - 1) ? _function->basicBlocks[i + 1] : 0; - _addrs.insert(_block, _codeNext - _codeStart); - - foreach (V4IR::Stmt *s, _block->statements) { - if (s->location.isValid()) { - QV4::LineNumberMapping mapping; - mapping.codeOffset = _codeNext - _codeStart; - mapping.lineNumber = s->location.startLine; - _vmFunction->lineNumberMappings.append(mapping); - } - - if (opt.isInSSA() && s->asTerminator()) { - foreach (const V4IR::Optimizer::SSADeconstructionMove &move, - opt.ssaDeconstructionMoves(_block)) { - Q_ASSERT(move.source->asTemp()); // FIXME: support Const exprs in Phi nodes. - if (move.needsConversion()) - convertType(move.source->asTemp(), move.target); - else - copyValue(move.source->asTemp(), move.target); - } - } - - _currentStatement = s; - s->accept(this); - } - } - - // TODO: patch stack size (the push instruction) - patchJumpAddresses(); - - _vmFunction->code = VME::exec; - _vmFunction->codeData = squeezeCode(); - - if (QV4::Debugging::Debugger *debugger = engine()->debugger) - debugger->setPendingBreakpoints(_vmFunction); - - qSwap(_currentStatement, cs); - qSwap(_stackSlotAllocator, stackSlotAllocator); - delete stackSlotAllocator; - qSwap(_function, function); - qSwap(_vmFunction, vmFunction); - qSwap(block, _block); - qSwap(nextBlock, _nextBlock); - qSwap(patches, _patches); - qSwap(addrs, _addrs); - qSwap(codeStart, _codeStart); - qSwap(codeNext, _codeNext); - qSwap(codeEnd, _codeEnd); - - delete[] codeStart; -} - -void InstructionSelection::callValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result) -{ - Instruction::CallValue call; - prepareCallArgs(args, call.argc, call.args); - call.dest = getParam(value); - call.result = getResultParam(result); - addInstruction(call); -} - -void InstructionSelection::callProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result) -{ - // call the property on the loaded base - Instruction::CallProperty call; - call.base = getParam(base); - call.name = identifier(name); - prepareCallArgs(args, call.argc, call.args); - call.result = getResultParam(result); - addInstruction(call); -} - -void InstructionSelection::callSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::ExprList *args, V4IR::Temp *result) -{ - // call the property on the loaded base - Instruction::CallElement call; - call.base = getParam(base); - call.index = getParam(index); - prepareCallArgs(args, call.argc, call.args); - call.result = getResultParam(result); - addInstruction(call); -} - -void InstructionSelection::convertType(V4IR::Temp *source, V4IR::Temp *target) -{ - if (_stackSlotAllocator) - _stackSlotAllocator->addHint(*source, *target); - - // FIXME: do something more useful with this info - copyValue(source, target); -} - -void InstructionSelection::constructActivationProperty(V4IR::Name *func, - V4IR::ExprList *args, - V4IR::Temp *result) -{ - Instruction::CreateActivationProperty create; - create.name = identifier(*func->id); - prepareCallArgs(args, create.argc, create.args); - create.result = getResultParam(result); - addInstruction(create); -} - -void InstructionSelection::constructProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result) -{ - Instruction::CreateProperty create; - create.base = getParam(base); - create.name = identifier(name); - prepareCallArgs(args, create.argc, create.args); - create.result = getResultParam(result); - addInstruction(create); -} - -void InstructionSelection::constructValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result) -{ - Instruction::CreateValue create; - create.func = getParam(value); - prepareCallArgs(args, create.argc, create.args); - create.result = getResultParam(result); - addInstruction(create); -} - -void InstructionSelection::loadThisObject(V4IR::Temp *temp) -{ - Instruction::LoadThis load; - load.result = getResultParam(temp); - addInstruction(load); -} - -void InstructionSelection::loadConst(V4IR::Const *sourceConst, V4IR::Temp *targetTemp) -{ - assert(sourceConst); - - Instruction::LoadValue load; - load.value = getParam(sourceConst); - load.result = getResultParam(targetTemp); - addInstruction(load); -} - -void InstructionSelection::loadString(const QString &str, V4IR::Temp *targetTemp) -{ - Instruction::LoadValue load; - load.value = Instr::Param::createValue(QV4::Value::fromString(identifier(str))); - load.result = getResultParam(targetTemp); - addInstruction(load); -} - -void InstructionSelection::loadRegexp(V4IR::RegExp *sourceRegexp, V4IR::Temp *targetTemp) -{ - QV4::Value v = QV4::Value::fromObject(engine()->newRegExpObject( - *sourceRegexp->value, - sourceRegexp->flags)); - _vmFunction->generatedValues.append(v); - - Instruction::LoadValue load; - load.value = Instr::Param::createValue(v); - load.result = getResultParam(targetTemp); - addInstruction(load); -} - -void InstructionSelection::getActivationProperty(const V4IR::Name *name, V4IR::Temp *temp) -{ - Instruction::LoadName load; - load.name = identifier(*name->id); - load.result = getResultParam(temp); - addInstruction(load); -} - -void InstructionSelection::setActivationProperty(V4IR::Temp *source, const QString &targetName) -{ - Instruction::StoreName store; - store.source = getParam(source); - store.name = identifier(targetName); - addInstruction(store); -} - -void InstructionSelection::initClosure(V4IR::Closure *closure, V4IR::Temp *target) -{ - QV4::Function *vmFunc = vmFunction(closure->value); - assert(vmFunc); - Instruction::LoadClosure load; - load.value = vmFunc; - load.result = getResultParam(target); - addInstruction(load); -} - -void InstructionSelection::getProperty(V4IR::Temp *base, const QString &name, V4IR::Temp *target) -{ - Instruction::LoadProperty load; - load.base = getParam(base); - load.name = identifier(name); - load.result = getResultParam(target); - addInstruction(load); -} - -void InstructionSelection::setProperty(V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName) -{ - Instruction::StoreProperty store; - store.base = getParam(targetBase); - store.name = identifier(targetName); - store.source = getParam(source); - addInstruction(store); -} - -void InstructionSelection::getElement(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *target) -{ - Instruction::LoadElement load; - load.base = getParam(base); - load.index = getParam(index); - load.result = getResultParam(target); - addInstruction(load); -} - -void InstructionSelection::setElement(V4IR::Temp *source, V4IR::Temp *targetBase, V4IR::Temp *targetIndex) -{ - Instruction::StoreElement store; - store.base = getParam(targetBase); - store.index = getParam(targetIndex); - store.source = getParam(source); - addInstruction(store); -} - -void InstructionSelection::copyValue(V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp) -{ - if (_stackSlotAllocator) - _stackSlotAllocator->addHint(*sourceTemp, *targetTemp); - - Instruction::MoveTemp move; - move.source = getParam(sourceTemp); - move.result = getResultParam(targetTemp); - if (move.source != move.result) - addInstruction(move); -} - -void InstructionSelection::unop(V4IR::AluOp oper, V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp) -{ - QV4::UnaryOpName op = 0; - switch (oper) { - case V4IR::OpIfTrue: assert(!"unreachable"); break; - case V4IR::OpNot: op = QV4::__qmljs_not; break; - case V4IR::OpUMinus: op = QV4::__qmljs_uminus; break; - case V4IR::OpUPlus: op = QV4::__qmljs_uplus; break; - case V4IR::OpCompl: op = QV4::__qmljs_compl; break; - case V4IR::OpIncrement: op = QV4::__qmljs_increment; break; - case V4IR::OpDecrement: op = QV4::__qmljs_decrement; break; - default: assert(!"unreachable"); break; - } // switch - - if (op) { - Instruction::Unop unop; - unop.alu = op; - unop.source = getParam(sourceTemp); - unop.result = getResultParam(targetTemp); - addInstruction(unop); - } else { - qWarning(" UNOP1"); - } -} - -void InstructionSelection::binop(V4IR::AluOp oper, V4IR::Expr *leftSource, V4IR::Expr *rightSource, V4IR::Temp *target) -{ -#ifdef USE_TYPE_INFO - if (leftSource->type & V4IR::NumberType && rightSource->type & V4IR::NumberType) { - // TODO: add Temp+Const variation on the topic. - switch (oper) { - case V4IR::OpAdd: { - Instruction::AddNumberParams instr; - instr.lhs = getParam(leftSource); - instr.rhs = getParam(rightSource); - instr.result = getResultParam(target); - addInstruction(instr); - } return; - case V4IR::OpMul: { - Instruction::MulNumberParams instr; - instr.lhs = getParam(leftSource); - instr.rhs = getParam(rightSource); - instr.result = getResultParam(target); - addInstruction(instr); - } return; - case V4IR::OpSub: { - Instruction::SubNumberParams instr; - instr.lhs = getParam(leftSource); - instr.rhs = getParam(rightSource); - instr.result = getResultParam(target); - addInstruction(instr); - } return; - default: - break; - } - } -#else // !USE_TYPE_INFO - Q_ASSERT(leftSource->asTemp() && rightSource->asTemp()); -#endif // USE_TYPE_INFO - - if (oper == V4IR::OpInstanceof || oper == V4IR::OpIn || oper == V4IR::OpAdd) { - Instruction::BinopContext binop; - if (oper == V4IR::OpInstanceof) - binop.alu = QV4::__qmljs_instanceof; - else if (oper == V4IR::OpIn) - binop.alu = QV4::__qmljs_in; - else - binop.alu = QV4::__qmljs_add; - binop.lhs = getParam(leftSource); - binop.rhs = getParam(rightSource); - binop.result = getResultParam(target); - addInstruction(binop); - } else { - Instruction::Binop binop; - binop.alu = aluOpFunction(oper); - binop.lhs = getParam(leftSource); - binop.rhs = getParam(rightSource); - binop.result = getResultParam(target); - addInstruction(binop); - } -} - -void InstructionSelection::inplaceNameOp(V4IR::AluOp oper, V4IR::Temp *rightSource, const QString &targetName) -{ - QV4::InplaceBinOpName op = 0; - switch (oper) { - case V4IR::OpBitAnd: op = QV4::__qmljs_inplace_bit_and_name; break; - case V4IR::OpBitOr: op = QV4::__qmljs_inplace_bit_or_name; break; - case V4IR::OpBitXor: op = QV4::__qmljs_inplace_bit_xor_name; break; - case V4IR::OpAdd: op = QV4::__qmljs_inplace_add_name; break; - case V4IR::OpSub: op = QV4::__qmljs_inplace_sub_name; break; - case V4IR::OpMul: op = QV4::__qmljs_inplace_mul_name; break; - case V4IR::OpDiv: op = QV4::__qmljs_inplace_div_name; break; - case V4IR::OpMod: op = QV4::__qmljs_inplace_mod_name; break; - case V4IR::OpLShift: op = QV4::__qmljs_inplace_shl_name; break; - case V4IR::OpRShift: op = QV4::__qmljs_inplace_shr_name; break; - case V4IR::OpURShift: op = QV4::__qmljs_inplace_ushr_name; break; - default: break; - } - - if (op) { - Instruction::InplaceNameOp ieo; - ieo.alu = op; - ieo.name = identifier(targetName); - ieo.source = getParam(rightSource); - addInstruction(ieo); - } -} - -void InstructionSelection::inplaceElementOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBaseTemp, V4IR::Temp *targetIndexTemp) -{ - QV4::InplaceBinOpElement op = 0; - switch (oper) { - case V4IR::OpBitAnd: op = QV4::__qmljs_inplace_bit_and_element; break; - case V4IR::OpBitOr: op = QV4::__qmljs_inplace_bit_or_element; break; - case V4IR::OpBitXor: op = QV4::__qmljs_inplace_bit_xor_element; break; - case V4IR::OpAdd: op = QV4::__qmljs_inplace_add_element; break; - case V4IR::OpSub: op = QV4::__qmljs_inplace_sub_element; break; - case V4IR::OpMul: op = QV4::__qmljs_inplace_mul_element; break; - case V4IR::OpDiv: op = QV4::__qmljs_inplace_div_element; break; - case V4IR::OpMod: op = QV4::__qmljs_inplace_mod_element; break; - case V4IR::OpLShift: op = QV4::__qmljs_inplace_shl_element; break; - case V4IR::OpRShift: op = QV4::__qmljs_inplace_shr_element; break; - case V4IR::OpURShift: op = QV4::__qmljs_inplace_ushr_element; break; - default: break; - } - - Instruction::InplaceElementOp ieo; - ieo.alu = op; - ieo.base = getParam(targetBaseTemp); - ieo.index = getParam(targetIndexTemp); - ieo.source = getParam(source); - addInstruction(ieo); -} - -void InstructionSelection::inplaceMemberOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName) -{ - QV4::InplaceBinOpMember op = 0; - switch (oper) { - case V4IR::OpBitAnd: op = QV4::__qmljs_inplace_bit_and_member; break; - case V4IR::OpBitOr: op = QV4::__qmljs_inplace_bit_or_member; break; - case V4IR::OpBitXor: op = QV4::__qmljs_inplace_bit_xor_member; break; - case V4IR::OpAdd: op = QV4::__qmljs_inplace_add_member; break; - case V4IR::OpSub: op = QV4::__qmljs_inplace_sub_member; break; - case V4IR::OpMul: op = QV4::__qmljs_inplace_mul_member; break; - case V4IR::OpDiv: op = QV4::__qmljs_inplace_div_member; break; - case V4IR::OpMod: op = QV4::__qmljs_inplace_mod_member; break; - case V4IR::OpLShift: op = QV4::__qmljs_inplace_shl_member; break; - case V4IR::OpRShift: op = QV4::__qmljs_inplace_shr_member; break; - case V4IR::OpURShift: op = QV4::__qmljs_inplace_ushr_member; break; - default: break; - } - - Instruction::InplaceMemberOp imo; - imo.alu = op; - imo.base = getParam(targetBase); - imo.member = identifier(targetName); - imo.source = getParam(source); - addInstruction(imo); -} - -void InstructionSelection::prepareCallArgs(V4IR::ExprList *e, quint32 &argc, quint32 &args) -{ - bool singleArgIsTemp = false; - if (e && e->next == 0 && e->expr->asTemp()) { - singleArgIsTemp = e->expr->asTemp()->kind == V4IR::Temp::VirtualRegister; - } - - if (singleArgIsTemp) { - // We pass single arguments as references to the stack, but only if it's not a local or an argument. - argc = 1; - args = getParam(e->expr).index; - } else if (e) { - // We need to move all the temps into the function arg array - int argLocation = outgoingArgumentTempStart(); - assert(argLocation >= 0); - argc = 0; - args = argLocation; - while (e) { - Instruction::MoveTemp move; - move.source = getParam(e->expr); - move.result = Instr::Param::createTemp(argLocation); - addInstruction(move); - ++argLocation; - ++argc; - e = e->next; - } - } else { - argc = 0; - args = 0; - } -} - -void InstructionSelection::visitJump(V4IR::Jump *s) -{ - if (s->target == _nextBlock) - return; - - Instruction::Jump jump; - jump.offset = 0; - ptrdiff_t loc = addInstruction(jump) + (((const char *)&jump.offset) - ((const char *)&jump)); - - _patches[s->target].append(loc); -} - -void InstructionSelection::visitCJump(V4IR::CJump *s) -{ - Instr::Param condition; - if (V4IR::Temp *t = s->cond->asTemp()) { - condition = getResultParam(t); - } else if (V4IR::Binop *b = s->cond->asBinop()) { - condition = getResultParam(0); - Instruction::Binop binop; - binop.alu = aluOpFunction(b->op); - binop.lhs = getParam(b->left); - binop.rhs = getParam(b->right); - binop.result = condition; - addInstruction(binop); - } else { - Q_UNIMPLEMENTED(); - } - - Instruction::CJump jump; - jump.offset = 0; - jump.condition = condition; - ptrdiff_t trueLoc = addInstruction(jump) + (((const char *)&jump.offset) - ((const char *)&jump)); - _patches[s->iftrue].append(trueLoc); - - if (s->iffalse != _nextBlock) { - Instruction::Jump jump; - jump.offset = 0; - ptrdiff_t falseLoc = addInstruction(jump) + (((const char *)&jump.offset) - ((const char *)&jump)); - _patches[s->iffalse].append(falseLoc); - } -} - -void InstructionSelection::visitRet(V4IR::Ret *s) -{ - Instruction::Ret ret; - ret.result = getParam(s->expr); - addInstruction(ret); -} - -void InstructionSelection::visitTry(V4IR::Try *t) -{ - Instruction::EnterTry enterTry; - enterTry.tryOffset = 0; - enterTry.catchOffset = 0; - enterTry.exceptionVarName = identifier(t->exceptionVarName); - enterTry.exceptionVar = getParam(t->exceptionVar); - ptrdiff_t enterTryLoc = addInstruction(enterTry); - - ptrdiff_t tryLoc = enterTryLoc + (((const char *)&enterTry.tryOffset) - ((const char *)&enterTry)); - _patches[t->tryBlock].append(tryLoc); - - ptrdiff_t catchLoc = enterTryLoc + (((const char *)&enterTry.catchOffset) - ((const char *)&enterTry)); - _patches[t->catchBlock].append(catchLoc); -} - -void InstructionSelection::callBuiltinInvalid(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result) -{ - Instruction::CallActivationProperty call; - call.name = identifier(*func->id); - prepareCallArgs(args, call.argc, call.args); - call.result = getResultParam(result); - addInstruction(call); -} - -void InstructionSelection::callBuiltinTypeofMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) -{ - Instruction::CallBuiltinTypeofMember call; - call.base = getParam(base); - call.member = identifier(name); - call.result = getResultParam(result); - addInstruction(call); -} - -void InstructionSelection::callBuiltinTypeofSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) -{ - Instruction::CallBuiltinTypeofSubscript call; - call.base = getParam(base); - call.index = getParam(index); - call.result = getResultParam(result); - addInstruction(call); -} - -void InstructionSelection::callBuiltinTypeofName(const QString &name, V4IR::Temp *result) -{ - Instruction::CallBuiltinTypeofName call; - call.name = identifier(name); - call.result = getResultParam(result); - addInstruction(call); -} - -void InstructionSelection::callBuiltinTypeofValue(V4IR::Temp *value, V4IR::Temp *result) -{ - Instruction::CallBuiltinTypeofValue call; - call.value = getParam(value); - call.result = getResultParam(result); - addInstruction(call); -} - -void InstructionSelection::callBuiltinDeleteMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) -{ - Instruction::CallBuiltinDeleteMember call; - call.base = getParam(base); - call.member = identifier(name); - call.result = getResultParam(result); - addInstruction(call); -} - -void InstructionSelection::callBuiltinDeleteSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) -{ - Instruction::CallBuiltinDeleteSubscript call; - call.base = getParam(base); - call.index = getParam(index); - call.result = getResultParam(result); - addInstruction(call); -} - -void InstructionSelection::callBuiltinDeleteName(const QString &name, V4IR::Temp *result) -{ - Instruction::CallBuiltinDeleteName call; - call.name = identifier(name); - call.result = getResultParam(result); - addInstruction(call); -} - -void InstructionSelection::callBuiltinDeleteValue(V4IR::Temp *result) -{ - Instruction::LoadValue load; - load.value = Instr::Param::createValue(QV4::Value::fromBoolean(false)); - load.result = getResultParam(result); - addInstruction(load); -} - -void InstructionSelection::callBuiltinPostDecrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) -{ - Instruction::CallBuiltinPostDecMember call; - call.base = getParam(base); - call.member = identifier(name); - call.result = getResultParam(result); - addInstruction(call); -} - -void InstructionSelection::callBuiltinPostDecrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) -{ - Instruction::CallBuiltinPostDecSubscript call; - call.base = getParam(base); - call.index = getParam(index); - call.result = getResultParam(result); - addInstruction(call); -} - -void InstructionSelection::callBuiltinPostDecrementName(const QString &name, V4IR::Temp *result) -{ - Instruction::CallBuiltinPostDecName call; - call.name = identifier(name); - call.result = getResultParam(result); - addInstruction(call); -} - -void InstructionSelection::callBuiltinPostDecrementValue(V4IR::Temp *value, V4IR::Temp *result) -{ - Instruction::CallBuiltinPostDecValue call; - call.value = getParam(value); - call.result = getResultParam(result); - addInstruction(call); -} - -void InstructionSelection::callBuiltinPostIncrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) -{ - Instruction::CallBuiltinPostIncMember call; - call.base = getParam(base); - call.member = identifier(name); - call.result = getResultParam(result); - addInstruction(call); -} - -void InstructionSelection::callBuiltinPostIncrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) -{ - Instruction::CallBuiltinPostIncSubscript call; - call.base = getParam(base); - call.index = getParam(index); - call.result = getResultParam(result); - addInstruction(call); -} - -void InstructionSelection::callBuiltinPostIncrementName(const QString &name, V4IR::Temp *result) -{ - Instruction::CallBuiltinPostIncName call; - call.name = identifier(name); - call.result = getResultParam(result); - addInstruction(call); -} - -void InstructionSelection::callBuiltinPostIncrementValue(V4IR::Temp *value, V4IR::Temp *result) -{ - Instruction::CallBuiltinPostIncValue call; - call.value = getParam(value); - call.result = getResultParam(result); - addInstruction(call); -} - -void InstructionSelection::callBuiltinThrow(V4IR::Temp *arg) -{ - Instruction::CallBuiltinThrow call; - call.arg = getParam(arg); - addInstruction(call); -} - -void InstructionSelection::callBuiltinFinishTry() -{ - Instruction::CallBuiltinFinishTry call; - addInstruction(call); -} - -void InstructionSelection::callBuiltinForeachIteratorObject(V4IR::Temp *arg, V4IR::Temp *result) -{ - Instruction::CallBuiltinForeachIteratorObject call; - call.arg = getParam(arg); - call.result = getResultParam(result); - addInstruction(call); -} - -void InstructionSelection::callBuiltinForeachNextPropertyname(V4IR::Temp *arg, V4IR::Temp *result) -{ - Instruction::CallBuiltinForeachNextPropertyName call; - call.arg = getParam(arg); - call.result = getResultParam(result); - addInstruction(call); -} - -void InstructionSelection::callBuiltinPushWithScope(V4IR::Temp *arg) -{ - Instruction::CallBuiltinPushScope call; - call.arg = getParam(arg); - addInstruction(call); -} - -void InstructionSelection::callBuiltinPopScope() -{ - Instruction::CallBuiltinPopScope call; - addInstruction(call); -} - -void InstructionSelection::callBuiltinDeclareVar(bool deletable, const QString &name) -{ - Instruction::CallBuiltinDeclareVar call; - call.isDeletable = deletable; - call.varName = identifier(name); - addInstruction(call); -} - -void InstructionSelection::callBuiltinDefineGetterSetter(V4IR::Temp *object, const QString &name, V4IR::Temp *getter, V4IR::Temp *setter) -{ - Instruction::CallBuiltinDefineGetterSetter call; - call.object = getParam(object); - call.name = identifier(name); - call.getter = getParam(getter); - call.setter = getParam(setter); - addInstruction(call); -} - -void InstructionSelection::callBuiltinDefineProperty(V4IR::Temp *object, const QString &name, V4IR::Temp *value) -{ - Instruction::CallBuiltinDefineProperty call; - call.object = getParam(object); - call.name = identifier(name); - call.value = getParam(value); - addInstruction(call); -} - -void InstructionSelection::callBuiltinDefineArray(V4IR::Temp *result, V4IR::ExprList *args) -{ - Instruction::CallBuiltinDefineArray call; - prepareCallArgs(args, call.argc, call.args); - call.result = getResultParam(result); - addInstruction(call); -} - -void InstructionSelection::callBuiltinDefineObjectLiteral(V4IR::Temp *result, V4IR::ExprList *args) -{ - int argLocation = outgoingArgumentTempStart(); - - QV4::InternalClass *klass = engine()->emptyClass; - V4IR::ExprList *it = args; - while (it) { - V4IR::Name *name = it->expr->asName(); - it = it->next; - - bool isData = it->expr->asConst()->value; - it = it->next; - klass = klass->addMember(identifier(*name->id), isData ? QV4::Attr_Data : QV4::Attr_Accessor); - - Instruction::MoveTemp move; - move.source = getParam(it->expr); - move.result = Instr::Param::createTemp(argLocation); - addInstruction(move); - ++argLocation; - - if (!isData) { - it = it->next; - - Instruction::MoveTemp move; - move.source = getParam(it->expr); - move.result = Instr::Param::createTemp(argLocation); - addInstruction(move); - ++argLocation; - } - - it = it->next; - } - - Instruction::CallBuiltinDefineObjectLiteral call; - call.internalClass = klass; - call.args = outgoingArgumentTempStart(); - call.result = getResultParam(result); - addInstruction(call); -} - -ptrdiff_t InstructionSelection::addInstructionHelper(Instr::Type type, Instr &instr) -{ -#ifdef MOTH_THREADED_INTERPRETER - instr.common.code = VME::instructionJumpTable()[static_cast(type)]; -#else - instr.common.instructionType = type; -#endif - instr.common.breakPoint = 0; - - int instructionSize = Instr::size(type); - if (_codeEnd - _codeNext < instructionSize) { - int currSize = _codeEnd - _codeStart; - uchar *newCode = new uchar[currSize * 2]; - ::memset(newCode + currSize, 0, currSize); - ::memcpy(newCode, _codeStart, currSize); - _codeNext = _codeNext - _codeStart + newCode; - delete[] _codeStart; - _codeStart = newCode; - _codeEnd = _codeStart + currSize * 2; - } - - ::memcpy(_codeNext, reinterpret_cast(&instr), instructionSize); - ptrdiff_t ptrOffset = _codeNext - _codeStart; - _codeNext += instructionSize; - - return ptrOffset; -} - -void InstructionSelection::patchJumpAddresses() -{ - typedef QHash >::ConstIterator PatchIt; - for (PatchIt i = _patches.begin(), ei = _patches.end(); i != ei; ++i) { - Q_ASSERT(_addrs.contains(i.key())); - ptrdiff_t target = _addrs.value(i.key()); - - const QVector &patchList = i.value(); - for (int ii = 0, eii = patchList.count(); ii < eii; ++ii) { - ptrdiff_t patch = patchList.at(ii); - - *((ptrdiff_t *)(_codeStart + patch)) = target - patch; - } - } - - _patches.clear(); - _addrs.clear(); -} - -uchar *InstructionSelection::squeezeCode() const -{ - int codeSize = _codeNext - _codeStart; - uchar *squeezed = new uchar[codeSize]; - ::memcpy(squeezed, _codeStart, codeSize); - return squeezed; -} - -QV4::String *InstructionSelection::identifier(const QString &s) -{ - QV4::String *str = engine()->newIdentifier(s); - _vmFunction->identifiers.append(str); - return str; -} - -Instr::Param InstructionSelection::getParam(V4IR::Expr *e) { - typedef Instr::Param Param; - assert(e); - - if (V4IR::Const *c = e->asConst()) { - return Param::createValue(convertToValue(c)); - } else if (V4IR::Temp *t = e->asTemp()) { - switch (t->kind) { - case V4IR::Temp::Formal: - case V4IR::Temp::ScopedFormal: return Param::createArgument(t->index, t->scope); - case V4IR::Temp::Local: return Param::createLocal(t->index); - case V4IR::Temp::ScopedLocal: return Param::createScopedLocal(t->index, t->scope); - case V4IR::Temp::VirtualRegister: - return Param::createTemp(_stackSlotAllocator ? - _stackSlotAllocator->stackSlotFor(t, _currentStatement) : t->index); - default: - Q_UNIMPLEMENTED(); - return Param(); - } - } else { - Q_UNIMPLEMENTED(); - return Param(); - } -} diff --git a/src/qml/qml/v4/moth/qv4isel_moth_p.h b/src/qml/qml/v4/moth/qv4isel_moth_p.h deleted file mode 100644 index 9c17245f67..0000000000 --- a/src/qml/qml/v4/moth/qv4isel_moth_p.h +++ /dev/null @@ -1,201 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QV4ISEL_MOTH_P_H -#define QV4ISEL_MOTH_P_H - -#include -#include -#include -#include -#include -#include "qv4instr_moth_p.h" - -QT_BEGIN_NAMESPACE - -namespace QQmlJS { -namespace Moth { - -class StackSlotAllocator; - -class Q_QML_EXPORT InstructionSelection: - public V4IR::IRDecoder, - public EvalInstructionSelection -{ -public: - InstructionSelection(QV4::ExecutionEngine *engine, V4IR::Module *module); - ~InstructionSelection(); - - virtual void run(QV4::Function *vmFunction, V4IR::Function *function); - -protected: - virtual void visitJump(V4IR::Jump *); - virtual void visitCJump(V4IR::CJump *); - virtual void visitRet(V4IR::Ret *); - virtual void visitTry(V4IR::Try *); - - virtual void callBuiltinInvalid(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result); - virtual void callBuiltinTypeofMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result); - virtual void callBuiltinTypeofSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result); - virtual void callBuiltinTypeofName(const QString &name, V4IR::Temp *result); - virtual void callBuiltinTypeofValue(V4IR::Temp *value, V4IR::Temp *result); - virtual void callBuiltinDeleteMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result); - virtual void callBuiltinDeleteSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result); - virtual void callBuiltinDeleteName(const QString &name, V4IR::Temp *result); - virtual void callBuiltinDeleteValue(V4IR::Temp *result); - virtual void callBuiltinPostDecrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result); - virtual void callBuiltinPostDecrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result); - virtual void callBuiltinPostDecrementName(const QString &name, V4IR::Temp *result); - virtual void callBuiltinPostDecrementValue(V4IR::Temp *value, V4IR::Temp *result); - virtual void callBuiltinPostIncrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result); - virtual void callBuiltinPostIncrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result); - virtual void callBuiltinPostIncrementName(const QString &name, V4IR::Temp *result); - virtual void callBuiltinPostIncrementValue(V4IR::Temp *value, V4IR::Temp *result); - virtual void callBuiltinThrow(V4IR::Temp *arg); - virtual void callBuiltinFinishTry(); - virtual void callBuiltinForeachIteratorObject(V4IR::Temp *arg, V4IR::Temp *result); - virtual void callBuiltinForeachNextPropertyname(V4IR::Temp *arg, V4IR::Temp *result); - virtual void callBuiltinPushWithScope(V4IR::Temp *arg); - virtual void callBuiltinPopScope(); - virtual void callBuiltinDeclareVar(bool deletable, const QString &name); - virtual void callBuiltinDefineGetterSetter(V4IR::Temp *object, const QString &name, V4IR::Temp *getter, V4IR::Temp *setter); - virtual void callBuiltinDefineProperty(V4IR::Temp *object, const QString &name, V4IR::Temp *value); - virtual void callBuiltinDefineArray(V4IR::Temp *result, V4IR::ExprList *args); - virtual void callBuiltinDefineObjectLiteral(V4IR::Temp *result, V4IR::ExprList *args); - virtual void callValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result); - virtual void callProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result); - virtual void callSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::ExprList *args, V4IR::Temp *result); - virtual void convertType(V4IR::Temp *source, V4IR::Temp *target); - virtual void constructActivationProperty(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result); - virtual void constructProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result); - virtual void constructValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result); - virtual void loadThisObject(V4IR::Temp *temp); - virtual void loadConst(V4IR::Const *sourceConst, V4IR::Temp *targetTemp); - virtual void loadString(const QString &str, V4IR::Temp *targetTemp); - virtual void loadRegexp(V4IR::RegExp *sourceRegexp, V4IR::Temp *targetTemp); - virtual void getActivationProperty(const V4IR::Name *name, V4IR::Temp *temp); - virtual void setActivationProperty(V4IR::Temp *source, const QString &targetName); - virtual void initClosure(V4IR::Closure *closure, V4IR::Temp *target); - virtual void getProperty(V4IR::Temp *base, const QString &name, V4IR::Temp *target); - virtual void setProperty(V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName); - virtual void getElement(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *target); - virtual void setElement(V4IR::Temp *source, V4IR::Temp *targetBase, V4IR::Temp *targetIndex); - virtual void copyValue(V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp); - virtual void unop(V4IR::AluOp oper, V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp); - virtual void binop(V4IR::AluOp oper, V4IR::Expr *leftSource, V4IR::Expr *rightSource, V4IR::Temp *target); - virtual void inplaceNameOp(V4IR::AluOp oper, V4IR::Temp *rightSource, const QString &targetName); - virtual void inplaceElementOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBaseTemp, V4IR::Temp *targetIndexTemp); - virtual void inplaceMemberOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName); - -private: - struct Instruction { -#define MOTH_INSTR_DATA_TYPEDEF(I, FMT) typedef InstrData I; - FOR_EACH_MOTH_INSTR(MOTH_INSTR_DATA_TYPEDEF) -#undef MOTH_INSTR_DATA_TYPEDEF - private: - Instruction(); - }; - - Instr::Param getParam(V4IR::Expr *e); - - Instr::Param getResultParam(V4IR::Temp *result) - { - if (result) - return getParam(result); - else - return Instr::Param::createTemp(scratchTempIndex()); - } - - void simpleMove(V4IR::Move *); - void prepareCallArgs(V4IR::ExprList *, quint32 &, quint32 &); - - int outgoingArgumentTempStart() const { return _function->tempCount; } - int scratchTempIndex() const { return outgoingArgumentTempStart() + _function->maxNumberOfArguments; } - int frameSize() const { return scratchTempIndex() + 1; } - - template - inline ptrdiff_t addInstruction(const InstrData &data); - ptrdiff_t addInstructionHelper(Instr::Type type, Instr &instr); - void patchJumpAddresses(); - uchar *squeezeCode() const; - - QV4::String *identifier(const QString &s); - - V4IR::Function *_function; - QV4::Function *_vmFunction; - V4IR::BasicBlock *_block; - V4IR::BasicBlock *_nextBlock; - - QHash > _patches; - QHash _addrs; - - uchar *_codeStart; - uchar *_codeNext; - uchar *_codeEnd; - - StackSlotAllocator *_stackSlotAllocator; - V4IR::Stmt *_currentStatement; -}; - -class Q_QML_EXPORT ISelFactory: public EvalISelFactory -{ -public: - virtual ~ISelFactory() {} - virtual EvalInstructionSelection *create(QV4::ExecutionEngine *engine, V4IR::Module *module) - { return new InstructionSelection(engine, module); } - virtual bool jitCompileRegexps() const - { return false; } -}; - -template -ptrdiff_t InstructionSelection::addInstruction(const InstrData &data) -{ - Instr genericInstr; - InstrMeta::setData(genericInstr, data); - return addInstructionHelper(static_cast(InstrT), genericInstr); -} - -} // namespace Moth -} // namespace QQmlJS - -QT_END_NAMESPACE - -#endif // QV4ISEL_MOTH_P_H diff --git a/src/qml/qml/v4/moth/qv4vme_moth.cpp b/src/qml/qml/v4/moth/qv4vme_moth.cpp deleted file mode 100644 index a06ce4139a..0000000000 --- a/src/qml/qml/v4/moth/qv4vme_moth.cpp +++ /dev/null @@ -1,596 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4vme_moth_p.h" -#include "qv4instr_moth_p.h" -#include -#include -#include -#include - -#include - -#include "qv4alloca_p.h" - -#ifdef DO_TRACE_INSTR -# define TRACE_INSTR(I) fprintf(stderr, "executing a %s\n", #I); -# define TRACE(n, str, ...) { char buf[4096]; snprintf(buf, 4096, str, __VA_ARGS__); fprintf(stderr, " %s : %s\n", #n, buf); } -#else -# define TRACE_INSTR(I) -# define TRACE(n, str, ...) -#endif // DO_TRACE_INSTR - -using namespace QQmlJS; -using namespace QQmlJS::Moth; - -#define MOTH_BEGIN_INSTR_COMMON(I) { \ - const InstrMeta<(int)Instr::I>::DataType &instr = InstrMeta<(int)Instr::I>::data(*genericInstr); \ - code += InstrMeta<(int)Instr::I>::Size; \ - if (context->engine->debugger && (instr.breakPoint || context->engine->debugger->pauseAtNextOpportunity())) \ - context->engine->debugger->maybeBreakAtInstruction(code, instr.breakPoint); \ - Q_UNUSED(instr); \ - TRACE_INSTR(I) - -#ifdef MOTH_THREADED_INTERPRETER - -# define MOTH_BEGIN_INSTR(I) op_##I: \ - MOTH_BEGIN_INSTR_COMMON(I) - -# define MOTH_NEXT_INSTR(I) { \ - genericInstr = reinterpret_cast(code); \ - goto *genericInstr->common.code; \ - } - -# define MOTH_END_INSTR(I) } \ - genericInstr = reinterpret_cast(code); \ - goto *genericInstr->common.code; \ - -#else - -# define MOTH_BEGIN_INSTR(I) \ - case Instr::I: \ - MOTH_BEGIN_INSTR_COMMON(I) - -# define MOTH_NEXT_INSTR(I) { \ - break; \ - } - -# define MOTH_END_INSTR(I) } \ - break; - -#endif - -#ifdef WITH_STATS -namespace { -struct VMStats { - quint64 paramIsValue; - quint64 paramIsArg; - quint64 paramIsLocal; - quint64 paramIsTemp; - quint64 paramIsScopedLocal; - - VMStats() - : paramIsValue(0) - , paramIsArg(0) - , paramIsLocal(0) - , paramIsTemp(0) - , paramIsScopedLocal(0) - {} - - ~VMStats() - { show(); } - - void show() { - fprintf(stderr, "VM stats:\n"); - fprintf(stderr, " value: %lu\n", paramIsValue); - fprintf(stderr, " arg: %lu\n", paramIsArg); - fprintf(stderr, " local: %lu\n", paramIsLocal); - fprintf(stderr, " temp: %lu\n", paramIsTemp); - fprintf(stderr, " scoped local: %lu\n", paramIsScopedLocal); - } -}; -static VMStats vmStats; -#define VMSTATS(what) ++vmStats.what -} -#else // !WITH_STATS -#define VMSTATS(what) {} -#endif // WITH_STATS - -static inline QV4::Value *getValueRef(QV4::ExecutionContext *context, - QV4::Value* stack, - const Instr::Param ¶m -#if !defined(QT_NO_DEBUG) - , unsigned stackSize -#endif - ) -{ -#ifdef DO_TRACE_INSTR - if (param.isValue()) { - fprintf(stderr, " value %s\n", param.value.toString(context)->toQString().toUtf8().constData()); - } else if (param.isArgument()) { - fprintf(stderr, " argument %d@%d\n", param.index, param.scope); - } else if (param.isLocal()) { - fprintf(stderr, " local %d\n", param.index); - } else if (param.isTemp()) { - fprintf(stderr, " temp %d\n", param.index); - } else if (param.isScopedLocal()) { - fprintf(stderr, " temp %d@%d\n", param.index, param.scope); - } else { - Q_ASSERT(!"INVALID"); - } -#endif // DO_TRACE_INSTR - - if (param.isValue()) { - VMSTATS(paramIsValue); - return const_cast(¶m.value); - } else if (param.isArgument()) { - VMSTATS(paramIsArg); - QV4::ExecutionContext *c = context; - uint scope = param.scope; - while (scope--) - c = c->outer; - QV4::CallContext *cc = static_cast(c); - const unsigned arg = param.index; - Q_ASSERT(arg >= 0); - Q_ASSERT((unsigned) arg < cc->argumentCount); - Q_ASSERT(cc->arguments); - return cc->arguments + arg; - } else if (param.isLocal()) { - VMSTATS(paramIsLocal); - const unsigned index = param.index; - QV4::CallContext *c = static_cast(context); - Q_ASSERT(index >= 0); - Q_ASSERT(index < context->variableCount()); - Q_ASSERT(c->locals); - return c->locals + index; - } else if (param.isTemp()) { - VMSTATS(paramIsTemp); - Q_ASSERT(param.index < stackSize); - return stack + param.index; - } else if (param.isScopedLocal()) { - VMSTATS(paramIsScopedLocal); - QV4::ExecutionContext *c = context; - uint scope = param.scope; - while (scope--) - c = c->outer; - const unsigned index = param.index; - QV4::CallContext *cc = static_cast(c); - Q_ASSERT(index >= 0); - Q_ASSERT(index < cc->variableCount()); - Q_ASSERT(cc->locals); - return cc->locals + index; - } else { - Q_UNIMPLEMENTED(); - return 0; - } -} - -#if defined(QT_NO_DEBUG) -# define VALUE(param) (*VALUEPTR(param)) - -// The non-temp case might need some tweaking for QML: there it would probably be a value instead of a local. -# define VALUEPTR(param) \ - (param.isTemp() ? stack + param.index \ - : (param.isLocal() ? static_cast(context)->locals + param.index \ - : getValueRef(context, stack, param))) -#else -# define VALUE(param) *getValueRef(context, stack, param, stackSize) -# define VALUEPTR(param) getValueRef(context, stack, param, stackSize) -#endif - -QV4::Value VME::run(QV4::ExecutionContext *context, const uchar *&code, - QV4::Value *stack, unsigned stackSize -#ifdef MOTH_THREADED_INTERPRETER - , void ***storeJumpTable -#endif - ) -{ -#ifdef DO_TRACE_INSTR - qDebug("Starting VME with context=%p and code=%p", context, code); -#endif // DO_TRACE_INSTR - -#ifdef MOTH_THREADED_INTERPRETER - if (storeJumpTable) { -#define MOTH_INSTR_ADDR(I, FMT) &&op_##I, - static void *jumpTable[] = { - FOR_EACH_MOTH_INSTR(MOTH_INSTR_ADDR) - }; -#undef MOTH_INSTR_ADDR - *storeJumpTable = jumpTable; - return QV4::Value::undefinedValue(); - } -#endif - - context->interpreterInstructionPointer = &code; - -#ifdef MOTH_THREADED_INTERPRETER - const Instr *genericInstr = reinterpret_cast(code); - goto *genericInstr->common.code; -#else - for (;;) { - const Instr *genericInstr = reinterpret_cast(code); - switch (genericInstr->common.instructionType) { -#endif - - MOTH_BEGIN_INSTR(MoveTemp) - VALUE(instr.result) = VALUE(instr.source); - MOTH_END_INSTR(MoveTemp) - - MOTH_BEGIN_INSTR(LoadValue) -// TRACE(value, "%s", instr.value.toString(context)->toQString().toUtf8().constData()); - VALUE(instr.result) = VALUE(instr.value); - MOTH_END_INSTR(LoadValue) - - MOTH_BEGIN_INSTR(LoadClosure) - __qmljs_init_closure(context, VALUEPTR(instr.result), instr.value); - MOTH_END_INSTR(LoadClosure) - - MOTH_BEGIN_INSTR(LoadName) - TRACE(inline, "property name = %s", instr.name->toQString().toUtf8().constData()); - __qmljs_get_activation_property(context, VALUEPTR(instr.result), instr.name); - MOTH_END_INSTR(LoadName) - - MOTH_BEGIN_INSTR(StoreName) - TRACE(inline, "property name = %s", instr.name->toQString().toUtf8().constData()); - __qmljs_set_activation_property(context, instr.name, VALUE(instr.source)); - MOTH_END_INSTR(StoreName) - - MOTH_BEGIN_INSTR(LoadElement) - __qmljs_get_element(context, VALUEPTR(instr.result), VALUE(instr.base), VALUE(instr.index)); - MOTH_END_INSTR(LoadElement) - - MOTH_BEGIN_INSTR(StoreElement) - __qmljs_set_element(context, VALUE(instr.base), VALUE(instr.index), VALUE(instr.source)); - MOTH_END_INSTR(StoreElement) - - MOTH_BEGIN_INSTR(LoadProperty) - __qmljs_get_property(context, VALUEPTR(instr.result), VALUE(instr.base), instr.name); - MOTH_END_INSTR(LoadProperty) - - MOTH_BEGIN_INSTR(StoreProperty) - __qmljs_set_property(context, VALUE(instr.base), instr.name, VALUE(instr.source)); - MOTH_END_INSTR(StoreProperty) - - MOTH_BEGIN_INSTR(Push) - TRACE(inline, "stack size: %u", instr.value); - stackSize = instr.value; - stack = static_cast(alloca(stackSize * sizeof(QV4::Value))); - memset(stack, 0, stackSize * sizeof(QV4::Value)); - MOTH_END_INSTR(Push) - - MOTH_BEGIN_INSTR(CallValue) -#ifdef DO_TRACE_INSTR - if (Debugging::Debugger *debugger = context->engine->debugger) { - if (QV4::FunctionObject *o = (VALUE(instr.dest)).asFunctionObject()) { - if (Debugging::FunctionDebugInfo *info = debugger->debugInfo(o)) { - QString n = debugger->name(o); - std::cerr << "*** Call to \"" << (n.isNull() ? "" : qPrintable(n)) << "\" defined @" << info->startLine << ":" << info->startColumn << std::endl; - } - } - } -#endif // DO_TRACE_INSTR - Q_ASSERT(instr.args + instr.argc <= stackSize); - QV4::Value *args = stack + instr.args; - __qmljs_call_value(context, VALUEPTR(instr.result), /*thisObject*/0, VALUE(instr.dest), args, instr.argc); - MOTH_END_INSTR(CallValue) - - MOTH_BEGIN_INSTR(CallProperty) - TRACE(property name, "%s, args=%u, argc=%u, this=%s", qPrintable(instr.name->toQString()), instr.args, instr.argc, (VALUE(instr.base)).toString(context)->toQString().toUtf8().constData()); - Q_ASSERT(instr.args + instr.argc <= stackSize); - QV4::Value *args = stack + instr.args; - __qmljs_call_property(context, VALUEPTR(instr.result), VALUE(instr.base), instr.name, args, instr.argc); - MOTH_END_INSTR(CallProperty) - - MOTH_BEGIN_INSTR(CallElement) - Q_ASSERT(instr.args + instr.argc <= stackSize); - QV4::Value *args = stack + instr.args; - __qmljs_call_element(context, VALUEPTR(instr.result), VALUE(instr.base), VALUE(instr.index), args, instr.argc); - MOTH_END_INSTR(CallElement) - - MOTH_BEGIN_INSTR(CallActivationProperty) - Q_ASSERT(instr.args + instr.argc <= stackSize); - TRACE(args, "starting at %d, length %d", instr.args, instr.argc); - QV4::Value *args = stack + instr.args; - __qmljs_call_activation_property(context, VALUEPTR(instr.result), instr.name, args, instr.argc); - MOTH_END_INSTR(CallActivationProperty) - - MOTH_BEGIN_INSTR(CallBuiltinThrow) - __qmljs_throw(context, VALUE(instr.arg)); - MOTH_END_INSTR(CallBuiltinThrow) - - MOTH_BEGIN_INSTR(EnterTry) - VALUE(instr.exceptionVar) = QV4::Value::undefinedValue(); - try { - const uchar *tryCode = ((uchar *)&instr.tryOffset) + instr.tryOffset; - run(context, tryCode, stack, stackSize); - code = tryCode; - context->interpreterInstructionPointer = &code; - } catch (QV4::Exception &ex) { - ex.accept(context); - VALUE(instr.exceptionVar) = ex.value(); - try { - QV4::ExecutionContext *catchContext = __qmljs_builtin_push_catch_scope(instr.exceptionVarName, ex.value(), context); - const uchar *catchCode = ((uchar *)&instr.catchOffset) + instr.catchOffset; - run(catchContext, catchCode, stack, stackSize); - code = catchCode; - context->interpreterInstructionPointer = &code; - context = __qmljs_builtin_pop_scope(catchContext); - } catch (QV4::Exception &ex) { - ex.accept(context); - VALUE(instr.exceptionVar) = ex.value(); - const uchar *catchCode = ((uchar *)&instr.catchOffset) + instr.catchOffset; - run(context, catchCode, stack, stackSize); - code = catchCode; - context->interpreterInstructionPointer = &code; - } - } - MOTH_END_INSTR(EnterTry) - - MOTH_BEGIN_INSTR(CallBuiltinFinishTry) - return QV4::Value(); - MOTH_END_INSTR(CallBuiltinFinishTry) - - MOTH_BEGIN_INSTR(CallBuiltinPushScope) - context = __qmljs_builtin_push_with_scope(VALUE(instr.arg), context); - MOTH_END_INSTR(CallBuiltinPushScope) - - MOTH_BEGIN_INSTR(CallBuiltinPopScope) - context = __qmljs_builtin_pop_scope(context); - MOTH_END_INSTR(CallBuiltinPopScope) - - MOTH_BEGIN_INSTR(CallBuiltinForeachIteratorObject) - __qmljs_foreach_iterator_object(context, VALUEPTR(instr.result), VALUE(instr.arg)); - MOTH_END_INSTR(CallBuiltinForeachIteratorObject) - - MOTH_BEGIN_INSTR(CallBuiltinForeachNextPropertyName) - __qmljs_foreach_next_property_name(VALUEPTR(instr.result), VALUE(instr.arg)); - MOTH_END_INSTR(CallBuiltinForeachNextPropertyName) - - MOTH_BEGIN_INSTR(CallBuiltinDeleteMember) - __qmljs_delete_member(context, VALUEPTR(instr.result), VALUE(instr.base), instr.member); - MOTH_END_INSTR(CallBuiltinDeleteMember) - - MOTH_BEGIN_INSTR(CallBuiltinDeleteSubscript) - __qmljs_delete_subscript(context, VALUEPTR(instr.result), VALUE(instr.base), VALUE(instr.index)); - MOTH_END_INSTR(CallBuiltinDeleteSubscript) - - MOTH_BEGIN_INSTR(CallBuiltinDeleteName) - __qmljs_delete_name(context, VALUEPTR(instr.result), instr.name); - MOTH_END_INSTR(CallBuiltinDeleteName) - - MOTH_BEGIN_INSTR(CallBuiltinTypeofMember) - __qmljs_builtin_typeof_member(context, VALUEPTR(instr.result), VALUE(instr.base), instr.member); - MOTH_END_INSTR(CallBuiltinTypeofMember) - - MOTH_BEGIN_INSTR(CallBuiltinTypeofSubscript) - __qmljs_builtin_typeof_element(context, VALUEPTR(instr.result), VALUE(instr.base), VALUE(instr.index)); - MOTH_END_INSTR(CallBuiltinTypeofSubscript) - - MOTH_BEGIN_INSTR(CallBuiltinTypeofName) - __qmljs_builtin_typeof_name(context, VALUEPTR(instr.result), instr.name); - MOTH_END_INSTR(CallBuiltinTypeofName) - - MOTH_BEGIN_INSTR(CallBuiltinTypeofValue) - __qmljs_builtin_typeof(context, VALUEPTR(instr.result), VALUE(instr.value)); - MOTH_END_INSTR(CallBuiltinTypeofValue) - - MOTH_BEGIN_INSTR(CallBuiltinPostIncMember) - __qmljs_builtin_post_increment_member(context, VALUEPTR(instr.result), VALUE(instr.base), instr.member); - MOTH_END_INSTR(CallBuiltinTypeofMember) - - MOTH_BEGIN_INSTR(CallBuiltinPostIncSubscript) - __qmljs_builtin_post_increment_element(context, VALUEPTR(instr.result), VALUE(instr.base), VALUEPTR(instr.index)); - MOTH_END_INSTR(CallBuiltinTypeofSubscript) - - MOTH_BEGIN_INSTR(CallBuiltinPostIncName) - __qmljs_builtin_post_increment_name(context, VALUEPTR(instr.result), instr.name); - MOTH_END_INSTR(CallBuiltinTypeofName) - - MOTH_BEGIN_INSTR(CallBuiltinPostIncValue) - __qmljs_builtin_post_increment(VALUEPTR(instr.result), VALUEPTR(instr.value)); - MOTH_END_INSTR(CallBuiltinTypeofValue) - - MOTH_BEGIN_INSTR(CallBuiltinPostDecMember) - __qmljs_builtin_post_decrement_member(context, VALUEPTR(instr.result), VALUE(instr.base), instr.member); - MOTH_END_INSTR(CallBuiltinTypeofMember) - - MOTH_BEGIN_INSTR(CallBuiltinPostDecSubscript) - __qmljs_builtin_post_decrement_element(context, VALUEPTR(instr.result), VALUE(instr.base), VALUE(instr.index)); - MOTH_END_INSTR(CallBuiltinTypeofSubscript) - - MOTH_BEGIN_INSTR(CallBuiltinPostDecName) - __qmljs_builtin_post_decrement_name(context, VALUEPTR(instr.result), instr.name); - MOTH_END_INSTR(CallBuiltinTypeofName) - - MOTH_BEGIN_INSTR(CallBuiltinPostDecValue) - __qmljs_builtin_post_decrement(VALUEPTR(instr.result), VALUEPTR(instr.value)); - MOTH_END_INSTR(CallBuiltinTypeofValue) - - MOTH_BEGIN_INSTR(CallBuiltinDeclareVar) - __qmljs_builtin_declare_var(context, instr.isDeletable, instr.varName); - MOTH_END_INSTR(CallBuiltinDeclareVar) - - MOTH_BEGIN_INSTR(CallBuiltinDefineGetterSetter) - __qmljs_builtin_define_getter_setter(context, VALUE(instr.object), instr.name, VALUEPTR(instr.getter), VALUEPTR(instr.setter)); - MOTH_END_INSTR(CallBuiltinDefineGetterSetter) - - MOTH_BEGIN_INSTR(CallBuiltinDefineProperty) - __qmljs_builtin_define_property(context, VALUE(instr.object), instr.name, VALUEPTR(instr.value)); - MOTH_END_INSTR(CallBuiltinDefineProperty) - - MOTH_BEGIN_INSTR(CallBuiltinDefineArray) - Q_ASSERT(instr.args + instr.argc <= stackSize); - QV4::Value *args = stack + instr.args; - __qmljs_builtin_define_array(context, VALUEPTR(instr.result), args, instr.argc); - MOTH_END_INSTR(CallBuiltinDefineArray) - - MOTH_BEGIN_INSTR(CallBuiltinDefineObjectLiteral) - QV4::Value *args = stack + instr.args; - __qmljs_builtin_define_object_literal(context, VALUEPTR(instr.result), args, instr.internalClass); - MOTH_END_INSTR(CallBuiltinDefineObjectLiteral) - - MOTH_BEGIN_INSTR(CreateValue) - Q_ASSERT(instr.args + instr.argc <= stackSize); - QV4::Value *args = stack + instr.args; - __qmljs_construct_value(context, VALUEPTR(instr.result), VALUE(instr.func), args, instr.argc); - MOTH_END_INSTR(CreateValue) - - MOTH_BEGIN_INSTR(CreateProperty) - Q_ASSERT(instr.args + instr.argc <= stackSize); - QV4::Value *args = stack + instr.args; - __qmljs_construct_property(context, VALUEPTR(instr.result), VALUE(instr.base), instr.name, args, instr.argc); - MOTH_END_INSTR(CreateProperty) - - MOTH_BEGIN_INSTR(CreateActivationProperty) - TRACE(inline, "property name = %s, args = %d, argc = %d", instr.name->toQString().toUtf8().constData(), instr.args, instr.argc); - Q_ASSERT(instr.args + instr.argc <= stackSize); - QV4::Value *args = stack + instr.args; - __qmljs_construct_activation_property(context, VALUEPTR(instr.result), instr.name, args, instr.argc); - MOTH_END_INSTR(CreateActivationProperty) - - MOTH_BEGIN_INSTR(Jump) - code = ((uchar *)&instr.offset) + instr.offset; - MOTH_END_INSTR(Jump) - - MOTH_BEGIN_INSTR(CJump) - uint cond = __qmljs_to_boolean(VALUE(instr.condition)); - TRACE(condition, "%s", cond ? "TRUE" : "FALSE"); - if (cond) - code = ((uchar *)&instr.offset) + instr.offset; - MOTH_END_INSTR(CJump) - - MOTH_BEGIN_INSTR(Unop) - instr.alu(VALUEPTR(instr.result), VALUE(instr.source)); - MOTH_END_INSTR(Unop) - - MOTH_BEGIN_INSTR(Binop) - instr.alu(VALUEPTR(instr.result), VALUE(instr.lhs), VALUE(instr.rhs)); - MOTH_END_INSTR(Binop) - - MOTH_BEGIN_INSTR(BinopContext) - instr.alu(context, VALUEPTR(instr.result), VALUE(instr.lhs), VALUE(instr.rhs)); - MOTH_END_INSTR(BinopContext) - - MOTH_BEGIN_INSTR(AddNumberParams) - QV4::Value lhs = VALUE(instr.lhs); - QV4::Value rhs = VALUE(instr.rhs); - if (lhs.isInteger() && rhs.isInteger()) - VALUE(instr.result) = QV4::add_int32(lhs.integerValue(), rhs.integerValue()); - else - VALUEPTR(instr.result)->setDouble(lhs.asDouble() + rhs.asDouble()); - MOTH_END_INSTR(AddNumberParams) - - MOTH_BEGIN_INSTR(MulNumberParams) - QV4::Value lhs = VALUE(instr.lhs); - QV4::Value rhs = VALUE(instr.rhs); - if (lhs.isInteger() && rhs.isInteger()) - VALUE(instr.result) = QV4::mul_int32(lhs.integerValue(), rhs.integerValue()); - else - VALUEPTR(instr.result)->setDouble(lhs.asDouble() * rhs.asDouble()); - MOTH_END_INSTR(MulNumberParams) - - MOTH_BEGIN_INSTR(SubNumberParams) - QV4::Value lhs = VALUE(instr.lhs); - QV4::Value rhs = VALUE(instr.rhs); - if (lhs.isInteger() && rhs.isInteger()) - VALUE(instr.result) = QV4::sub_int32(lhs.integerValue(), rhs.integerValue()); - else - VALUEPTR(instr.result)->setDouble(lhs.asDouble() - rhs.asDouble()); - MOTH_END_INSTR(SubNumberParams) - - MOTH_BEGIN_INSTR(Ret) - QV4::Value &result = VALUE(instr.result); -// TRACE(Ret, "returning value %s", result.toString(context)->toQString().toUtf8().constData()); - return result; - MOTH_END_INSTR(Ret) - - MOTH_BEGIN_INSTR(LoadThis) - VALUE(instr.result) = context->thisObject; - MOTH_END_INSTR(LoadThis) - - MOTH_BEGIN_INSTR(InplaceElementOp) - instr.alu(context, - VALUE(instr.base), - VALUE(instr.index), - VALUE(instr.source)); - MOTH_END_INSTR(InplaceElementOp) - - MOTH_BEGIN_INSTR(InplaceMemberOp) - instr.alu(context, - VALUE(instr.base), - instr.member, - VALUE(instr.source)); - MOTH_END_INSTR(InplaceMemberOp) - - MOTH_BEGIN_INSTR(InplaceNameOp) - TRACE(name, "%s", instr.name->toQString().toUtf8().constData()); - instr.alu(context, instr.name, VALUE(instr.source)); - MOTH_END_INSTR(InplaceNameOp) - -#ifdef MOTH_THREADED_INTERPRETER - // nothing to do -#else - default: - qFatal("QQmlJS::Moth::VME: Internal error - unknown instruction %d", genericInstr->common.instructionType); - break; - } - } -#endif - -} - -#ifdef MOTH_THREADED_INTERPRETER -void **VME::instructionJumpTable() -{ - static void **jumpTable = 0; - if (!jumpTable) { - const uchar *code = 0; - VME().run(0, code, 0, 0, &jumpTable); - } - return jumpTable; -} -#endif - -QV4::Value VME::exec(QV4::ExecutionContext *ctxt, const uchar *code) -{ - VME vme; - return vme.run(ctxt, code); -} diff --git a/src/qml/qml/v4/moth/qv4vme_moth_p.h b/src/qml/qml/v4/moth/qv4vme_moth_p.h deleted file mode 100644 index 59692500ba..0000000000 --- a/src/qml/qml/v4/moth/qv4vme_moth_p.h +++ /dev/null @@ -1,76 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QV4VME_MOTH_P_H -#define QV4VME_MOTH_P_H - -#include -#include "qv4instr_moth_p.h" - -QT_BEGIN_NAMESPACE - -namespace QQmlJS { -namespace Moth { - -class VME -{ -public: - static QV4::Value exec(QV4::ExecutionContext *, const uchar *); - -#ifdef MOTH_THREADED_INTERPRETER - static void **instructionJumpTable(); -#endif - -private: - QV4::Value run(QV4::ExecutionContext *, const uchar *&code, - QV4::Value *stack = 0, unsigned stackSize = 0 -#ifdef MOTH_THREADED_INTERPRETER - , void ***storeJumpTable = 0 -#endif - ); -}; - -} // namespace Moth -} // namespace QQmlJS - -QT_END_NAMESPACE - -#endif // QV4VME_MOTH_P_H diff --git a/src/qml/qml/v4/qv4alloca_p.h b/src/qml/qml/v4/qv4alloca_p.h deleted file mode 100644 index e4580da3d8..0000000000 --- a/src/qml/qml/v4/qv4alloca_p.h +++ /dev/null @@ -1,54 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QV4_ALLOCA_H -#define QV4_ALLOCA_H - -#include - -#if defined(Q_OS_WIN) -#include -#define alloca _alloca -#else -#include -#endif - -#endif diff --git a/src/qml/qml/v4/qv4argumentsobject.cpp b/src/qml/qml/v4/qv4argumentsobject.cpp deleted file mode 100644 index 6247ef1504..0000000000 --- a/src/qml/qml/v4/qv4argumentsobject.cpp +++ /dev/null @@ -1,171 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#include - -using namespace QV4; - -static Value throwTypeError(SimpleCallContext *ctx) -{ - ctx->throwTypeError(); - return Value::undefinedValue(); -} - -DEFINE_MANAGED_VTABLE(ArgumentsObject); - -ArgumentsObject::ArgumentsObject(CallContext *context, int formalParameterCount, int actualParameterCount) - : Object(context->engine), context(context) -{ - vtbl = &static_vtbl; - type = Type_ArgumentsObject; - - defineDefaultProperty(context->engine->id_length, Value::fromInt32(actualParameterCount)); - if (context->strictMode) { - for (uint i = 0; i < context->argumentCount; ++i) - Object::put(context, QString::number(i), context->arguments[i]); - FunctionObject *thrower = context->engine->newBuiltinFunction(context, 0, throwTypeError); - Property pd = Property::fromAccessor(thrower, thrower); - __defineOwnProperty__(context, QStringLiteral("callee"), pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); - __defineOwnProperty__(context, QStringLiteral("caller"), pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); - } else { - uint numAccessors = qMin(formalParameterCount, actualParameterCount); - context->engine->requireArgumentsAccessors(numAccessors); - for (uint i = 0; i < (uint)numAccessors; ++i) { - mappedArguments.append(context->argument(i)); - __defineOwnProperty__(context, i, context->engine->argumentsAccessors.at(i), Attr_Accessor); - } - for (uint i = numAccessors; i < qMin((uint)actualParameterCount, context->argumentCount); ++i) { - Property pd = Property::fromValue(context->argument(i)); - __defineOwnProperty__(context, i, pd, Attr_Data); - } - defineDefaultProperty(context, QStringLiteral("callee"), Value::fromObject(context->function)); - isNonStrictArgumentsObject = true; - } -} - -void ArgumentsObject::destroy(Managed *that) -{ - static_cast(that)->~ArgumentsObject(); -} - -bool ArgumentsObject::defineOwnProperty(ExecutionContext *ctx, uint index, const Property &desc, PropertyAttributes attrs) -{ - uint pidx = propertyIndexFromArrayIndex(index); - Property *pd = arrayData + pidx; - Property map; - PropertyAttributes mapAttrs; - bool isMapped = false; - if (pd && index < (uint)mappedArguments.size()) - isMapped = arrayAttributes && arrayAttributes[pidx].isAccessor() && pd->getter() == context->engine->argumentsAccessors.at(index).getter(); - - if (isMapped) { - map = *pd; - mapAttrs = arrayAttributes[pidx]; - arrayAttributes[pidx] = Attr_Data; - pd->value = mappedArguments.at(index); - } - - isNonStrictArgumentsObject = false; - bool strict = ctx->strictMode; - ctx->strictMode = false; - bool result = Object::__defineOwnProperty__(ctx, index, desc, attrs); - ctx->strictMode = strict; - isNonStrictArgumentsObject = true; - - if (isMapped && attrs.isData()) { - if (!attrs.isGeneric()) { - Value arg = desc.value; - map.setter()->call(Value::fromObject(this), &arg, 1); - } - if (attrs.isWritable()) { - *pd = map; - arrayAttributes[pidx] = mapAttrs; - } - } - - if (ctx->strictMode && !result) - ctx->throwTypeError(); - return result; -} - -DEFINE_MANAGED_VTABLE(ArgumentsGetterFunction); - -Value ArgumentsGetterFunction::call(Managed *getter, const Value &thisObject, Value *, int) -{ - ArgumentsGetterFunction *g = static_cast(getter); - Object *that = thisObject.asObject(); - if (!that) - getter->engine()->current->throwTypeError(); - ArgumentsObject *o = that->asArgumentsObject(); - if (!o) - getter->engine()->current->throwTypeError(); - - assert(g->index < o->context->argumentCount); - return o->context->argument(g->index); -} - -DEFINE_MANAGED_VTABLE(ArgumentsSetterFunction); - -Value ArgumentsSetterFunction::call(Managed *setter, const Value &thisObject, Value *args, int argc) -{ - ArgumentsSetterFunction *s = static_cast(setter); - Object *that = thisObject.asObject(); - if (!that) - setter->engine()->current->throwTypeError(); - ArgumentsObject *o = that->asArgumentsObject(); - if (!o) - setter->engine()->current->throwTypeError(); - - assert(s->index < o->context->argumentCount); - o->context->arguments[s->index] = argc ? args[0] : Value::undefinedValue(); - return Value::undefinedValue(); -} - -void ArgumentsObject::markObjects(Managed *that) -{ - ArgumentsObject *o = static_cast(that); - o->context->mark(); - for (int i = 0; i < o->mappedArguments.size(); ++i) { - Managed *m = o->mappedArguments.at(i).asManaged(); - if (m) - m->mark(); - } - Object::markObjects(that); -} diff --git a/src/qml/qml/v4/qv4argumentsobject_p.h b/src/qml/qml/v4/qv4argumentsobject_p.h deleted file mode 100644 index 3d760b8937..0000000000 --- a/src/qml/qml/v4/qv4argumentsobject_p.h +++ /dev/null @@ -1,97 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QV4ARGUMENTSOBJECTS_H -#define QV4ARGUMENTSOBJECTS_H - -#include "qv4object_p.h" -#include "qv4functionobject_p.h" - -QT_BEGIN_NAMESPACE - -namespace QV4 { - -struct ArgumentsGetterFunction: FunctionObject -{ - uint index; - - ArgumentsGetterFunction(ExecutionContext *scope, uint index) - : FunctionObject(scope), index(index) { vtbl = &static_vtbl; } - - static Value call(Managed *that, const Value &, Value *, int); - -protected: - static const ManagedVTable static_vtbl; -}; - -struct ArgumentsSetterFunction: FunctionObject -{ - uint index; - - ArgumentsSetterFunction(ExecutionContext *scope, uint index) - : FunctionObject(scope), index(index) { vtbl = &static_vtbl; } - - static Value call(Managed *that, const Value &, Value *, int); - -protected: - static const ManagedVTable static_vtbl; -}; - - -struct ArgumentsObject: Object { - CallContext *context; - QVector mappedArguments; - ArgumentsObject(CallContext *context, int formalParameterCount, int actualParameterCount); - ~ArgumentsObject() {} - - bool defineOwnProperty(ExecutionContext *ctx, uint index, const Property &desc, PropertyAttributes attrs); - - static void markObjects(Managed *that); -protected: - static const ManagedVTable static_vtbl; - static void destroy(Managed *); -}; - -} - -QT_END_NAMESPACE - -#endif - diff --git a/src/qml/qml/v4/qv4arrayobject.cpp b/src/qml/qml/v4/qv4arrayobject.cpp deleted file mode 100644 index b1f25177dd..0000000000 --- a/src/qml/qml/v4/qv4arrayobject.cpp +++ /dev/null @@ -1,865 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4arrayobject_p.h" -#include "qv4sparsearray_p.h" - -using namespace QV4; - -DEFINE_MANAGED_VTABLE(ArrayCtor); - -ArrayCtor::ArrayCtor(ExecutionContext *scope) - : FunctionObject(scope, scope->engine->newIdentifier(QStringLiteral("Array"))) -{ - vtbl = &static_vtbl; -} - -Value ArrayCtor::construct(Managed *m, Value *argv, int argc) -{ - ExecutionEngine *v4 = m->engine(); - ArrayObject *a = v4->newArrayObject(); - uint len; - if (argc == 1 && argv[0].isNumber()) { - bool ok; - len = argv[0].asArrayLength(&ok); - - if (!ok) - v4->current->throwRangeError(argv[0]); - - if (len < 0x1000) - a->arrayReserve(len); - } else { - len = argc; - a->arrayReserve(len); - for (unsigned int i = 0; i < len; ++i) - a->arrayData[i].value = argv[i]; - a->arrayDataLen = len; - } - a->setArrayLengthUnchecked(len); - - return Value::fromObject(a); -} - -Value ArrayCtor::call(Managed *that, const Value &, Value *argv, int argc) -{ - return construct(that, argv, argc); -} - -ArrayPrototype::ArrayPrototype(ExecutionContext *context) - : ArrayObject(context->engine) -{ -} - -void ArrayPrototype::init(ExecutionContext *ctx, const Value &ctor) -{ - ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); - ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); - ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("isArray"), method_isArray, 1); - defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); - defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); - defineDefaultProperty(ctx, QStringLiteral("toLocaleString"), method_toLocaleString, 0); - defineDefaultProperty(ctx, QStringLiteral("concat"), method_concat, 1); - defineDefaultProperty(ctx, QStringLiteral("join"), method_join, 1); - defineDefaultProperty(ctx, QStringLiteral("pop"), method_pop, 0); - defineDefaultProperty(ctx, QStringLiteral("push"), method_push, 1); - defineDefaultProperty(ctx, QStringLiteral("reverse"), method_reverse, 0); - defineDefaultProperty(ctx, QStringLiteral("shift"), method_shift, 0); - defineDefaultProperty(ctx, QStringLiteral("slice"), method_slice, 2); - defineDefaultProperty(ctx, QStringLiteral("sort"), method_sort, 1); - defineDefaultProperty(ctx, QStringLiteral("splice"), method_splice, 2); - defineDefaultProperty(ctx, QStringLiteral("unshift"), method_unshift, 1); - defineDefaultProperty(ctx, QStringLiteral("indexOf"), method_indexOf, 1); - defineDefaultProperty(ctx, QStringLiteral("lastIndexOf"), method_lastIndexOf, 1); - defineDefaultProperty(ctx, QStringLiteral("every"), method_every, 1); - defineDefaultProperty(ctx, QStringLiteral("some"), method_some, 1); - defineDefaultProperty(ctx, QStringLiteral("forEach"), method_forEach, 1); - defineDefaultProperty(ctx, QStringLiteral("map"), method_map, 1); - defineDefaultProperty(ctx, QStringLiteral("filter"), method_filter, 1); - defineDefaultProperty(ctx, QStringLiteral("reduce"), method_reduce, 1); - defineDefaultProperty(ctx, QStringLiteral("reduceRight"), method_reduceRight, 1); -} - -uint ArrayPrototype::getLength(ExecutionContext *ctx, Object *o) -{ - if (o->isArrayObject()) - return o->arrayLength(); - return o->get(ctx->engine->id_length).toUInt32(); -} - -Value ArrayPrototype::method_isArray(SimpleCallContext *ctx) -{ - Value arg = ctx->argument(0); - bool isArray = arg.asArrayObject(); - return Value::fromBoolean(isArray); -} - -Value ArrayPrototype::method_toString(SimpleCallContext *ctx) -{ - return method_join(ctx); -} - -Value ArrayPrototype::method_toLocaleString(SimpleCallContext *ctx) -{ - return method_toString(ctx); -} - -Value ArrayPrototype::method_concat(SimpleCallContext *ctx) -{ - ArrayObject *result = ctx->engine->newArrayObject(); - - if (ArrayObject *instance = ctx->thisObject.asArrayObject()) { - result->copyArrayData(instance); - } else { - QString v = ctx->thisObject.toString(ctx)->toQString(); - result->arraySet(0, Value::fromString(ctx, v)); - } - - for (uint i = 0; i < ctx->argumentCount; ++i) { - Value arg = ctx->argument(i); - - if (ArrayObject *elt = arg.asArrayObject()) - result->arrayConcat(elt); - - else - result->arraySet(getLength(ctx, result), arg); - } - - return Value::fromObject(result); -} - -Value ArrayPrototype::method_join(SimpleCallContext *ctx) -{ - Value arg = ctx->argument(0); - - QString r4; - if (arg.isUndefined()) - r4 = QStringLiteral(","); - else - r4 = arg.toString(ctx)->toQString(); - - Value self = ctx->thisObject; - const Value length = self.property(ctx, ctx->engine->id_length); - const quint32 r2 = Value::toUInt32(length.isUndefined() ? 0 : length.toNumber()); - - static QSet visitedArrayElements; - - if (! r2 || visitedArrayElements.contains(self.objectValue())) - return Value::fromString(ctx, QString()); - - // avoid infinite recursion - visitedArrayElements.insert(self.objectValue()); - - QString R; - - // ### FIXME - if (ArrayObject *a = self.asArrayObject()) { - for (uint i = 0; i < a->arrayLength(); ++i) { - if (i) - R += r4; - - Value e = a->getIndexed(i); - if (! (e.isUndefined() || e.isNull())) - R += e.toString(ctx)->toQString(); - } - } else { - // - // crazy! - // - Value r6 = self.property(ctx, ctx->engine->newString(QStringLiteral("0"))); - if (!(r6.isUndefined() || r6.isNull())) - R = r6.toString(ctx)->toQString(); - - for (quint32 k = 1; k < r2; ++k) { - R += r4; - - String *name = Value::fromDouble(k).toString(ctx); - Value r12 = self.property(ctx, name); - - if (! (r12.isUndefined() || r12.isNull())) - R += r12.toString(ctx)->toQString(); - } - } - - visitedArrayElements.remove(self.objectValue()); - return Value::fromString(ctx, R); -} - -Value ArrayPrototype::method_pop(SimpleCallContext *ctx) -{ - Object *instance = ctx->thisObject.toObject(ctx); - uint len = getLength(ctx, instance); - - if (!len) { - if (!instance->isArrayObject()) - instance->put(ctx->engine->id_length, Value::fromInt32(0)); - return Value::undefinedValue(); - } - - Value result = instance->getIndexed(len - 1); - - instance->deleteIndexedProperty(len - 1); - if (instance->isArrayObject()) - instance->setArrayLengthUnchecked(len - 1); - else - instance->put(ctx->engine->id_length, Value::fromDouble(len - 1)); - return result; -} - -Value ArrayPrototype::method_push(SimpleCallContext *ctx) -{ - Object *instance = ctx->thisObject.toObject(ctx); - uint len = getLength(ctx, instance); - - if (len + ctx->argumentCount < len) { - // ughh... - double l = len; - for (double i = 0; i < ctx->argumentCount; ++i) { - Value idx = Value::fromDouble(l + i); - instance->put(idx.toString(ctx), ctx->argument(i)); - } - double newLen = l + ctx->argumentCount; - if (!instance->isArrayObject()) - instance->put(ctx->engine->id_length, Value::fromDouble(newLen)); - else - ctx->throwRangeError(Value::fromString(ctx, QStringLiteral("Array.prototype.push: Overflow"))); - return Value::fromDouble(newLen); - } - - bool protoHasArray = false; - Object *p = instance; - while ((p = p->prototype)) - if (p->arrayDataLen) - protoHasArray = true; - - if (!protoHasArray && instance->arrayDataLen <= len) { - for (uint i = 0; i < ctx->argumentCount; ++i) { - Value v = ctx->argument(i); - - if (!instance->sparseArray) { - if (len >= instance->arrayAlloc) - instance->arrayReserve(len + 1); - instance->arrayData[len].value = v; - if (instance->arrayAttributes) - instance->arrayAttributes[len] = Attr_Data; - instance->arrayDataLen = len + 1; - } else { - uint i = instance->allocArrayValue(v); - instance->sparseArray->push_back(i, len); - } - ++len; - } - } else { - for (uint i = 0; i < ctx->argumentCount; ++i) - instance->putIndexed(len + i, ctx->argument(i)); - len += ctx->argumentCount; - } - if (instance->isArrayObject()) - instance->setArrayLengthUnchecked(len); - else - instance->put(ctx->engine->id_length, Value::fromDouble(len)); - - if (len < INT_MAX) - return Value::fromInt32(len); - return Value::fromDouble((double)len); - -} - -Value ArrayPrototype::method_reverse(SimpleCallContext *ctx) -{ - Object *instance = ctx->thisObject.toObject(ctx); - uint length = getLength(ctx, instance); - - int lo = 0, hi = length - 1; - - for (; lo < hi; ++lo, --hi) { - bool loExists, hiExists; - Value lval = instance->getIndexed(lo, &loExists); - Value hval = instance->getIndexed(hi, &hiExists); - if (hiExists) - instance->putIndexed(lo, hval); - else - instance->deleteIndexedProperty(lo); - if (loExists) - instance->putIndexed(hi, lval); - else - instance->deleteIndexedProperty(hi); - } - return Value::fromObject(instance); -} - -Value ArrayPrototype::method_shift(SimpleCallContext *ctx) -{ - Object *instance = ctx->thisObject.toObject(ctx); - uint len = getLength(ctx, instance); - - if (!len) { - if (!instance->isArrayObject()) - instance->put(ctx->engine->id_length, Value::fromInt32(0)); - return Value::undefinedValue(); - } - - Property *front = 0; - uint pidx = instance->propertyIndexFromArrayIndex(0); - if (pidx < UINT_MAX && (!instance->arrayAttributes || !instance->arrayAttributes[0].isGeneric())) - front = instance->arrayData + pidx; - - Value result = front ? instance->getValue(front, instance->arrayAttributes ? instance->arrayAttributes[pidx] : Attr_Data) : Value::undefinedValue(); - - bool protoHasArray = false; - Object *p = instance; - while ((p = p->prototype)) - if (p->arrayDataLen) - protoHasArray = true; - - if (!protoHasArray && instance->arrayDataLen <= len) { - if (!instance->sparseArray) { - if (instance->arrayDataLen) { - ++instance->arrayOffset; - ++instance->arrayData; - --instance->arrayDataLen; - --instance->arrayAlloc; - if (instance->arrayAttributes) - ++instance->arrayAttributes; - } - } else { - uint idx = instance->sparseArray->pop_front(); - instance->freeArrayValue(idx); - } - } else { - // do it the slow way - for (uint k = 1; k < len; ++k) { - bool exists; - Value v = instance->getIndexed(k, &exists); - if (exists) - instance->putIndexed(k - 1, v); - else - instance->deleteIndexedProperty(k - 1); - } - instance->deleteIndexedProperty(len - 1); - } - - if (instance->isArrayObject()) - instance->setArrayLengthUnchecked(len - 1); - else - instance->put(ctx->engine->id_length, Value::fromDouble(len - 1)); - return result; -} - -Value ArrayPrototype::method_slice(SimpleCallContext *ctx) -{ - Object *o = ctx->thisObject.toObject(ctx); - - ArrayObject *result = ctx->engine->newArrayObject(); - uint len = o->get(ctx->engine->id_length).toUInt32(); - double s = ctx->argument(0).toInteger(); - uint start; - if (s < 0) - start = (uint)qMax(len + s, 0.); - else if (s > len) - start = len; - else - start = (uint) s; - uint end = len; - if (!ctx->argument(1).isUndefined()) { - double e = ctx->argument(1).toInteger(); - if (e < 0) - end = (uint)qMax(len + e, 0.); - else if (e > len) - end = len; - else - end = (uint) e; - } - - uint n = 0; - for (uint i = start; i < end; ++i) { - bool exists; - Value v = o->getIndexed(i, &exists); - if (exists) { - result->arraySet(n, v); - } - ++n; - } - return Value::fromObject(result); -} - -Value ArrayPrototype::method_sort(SimpleCallContext *ctx) -{ - Object *instance = ctx->thisObject.toObject(ctx); - - uint len = getLength(ctx, instance); - - Value comparefn = ctx->argument(0); - instance->arraySort(ctx, instance, comparefn, len); - return ctx->thisObject; -} - -Value ArrayPrototype::method_splice(SimpleCallContext *ctx) -{ - Object *instance = ctx->thisObject.toObject(ctx); - uint len = getLength(ctx, instance); - - ArrayObject *newArray = ctx->engine->newArrayObject(); - - double rs = ctx->argument(0).toInteger(); - uint start; - if (rs < 0) - start = (uint) qMax(0., len + rs); - else - start = (uint) qMin(rs, (double)len); - - uint deleteCount = (uint)qMin(qMax(ctx->argument(1).toInteger(), 0.), (double)(len - start)); - - newArray->arrayReserve(deleteCount); - Property *pd = newArray->arrayData; - for (uint i = 0; i < deleteCount; ++i) { - pd->value = instance->getIndexed(start + i); - ++pd; - } - newArray->arrayDataLen = deleteCount; - newArray->setArrayLengthUnchecked(deleteCount); - - uint itemCount = ctx->argumentCount < 2 ? 0 : ctx->argumentCount - 2; - - if (itemCount < deleteCount) { - for (uint k = start; k < len - deleteCount; ++k) { - bool exists; - Value v = instance->getIndexed(k + deleteCount, &exists); - if (exists) - instance->putIndexed(k + itemCount, v); - else - instance->deleteIndexedProperty(k + itemCount); - } - for (uint k = len; k > len - deleteCount + itemCount; --k) - instance->deleteIndexedProperty(k - 1); - } else if (itemCount > deleteCount) { - uint k = len - deleteCount; - while (k > start) { - bool exists; - Value v = instance->getIndexed(k + deleteCount - 1, &exists); - if (exists) - instance->putIndexed(k + itemCount - 1, v); - else - instance->deleteIndexedProperty(k + itemCount - 1); - --k; - } - } - - for (uint i = 0; i < itemCount; ++i) - instance->putIndexed(start + i, ctx->argument(i + 2)); - - ctx->strictMode = true; - instance->put(ctx->engine->id_length, Value::fromDouble(len - deleteCount + itemCount)); - - return Value::fromObject(newArray); -} - -Value ArrayPrototype::method_unshift(SimpleCallContext *ctx) -{ - Object *instance = ctx->thisObject.toObject(ctx); - uint len = getLength(ctx, instance); - - bool protoHasArray = false; - Object *p = instance; - while ((p = p->prototype)) - if (p->arrayDataLen) - protoHasArray = true; - - if (!protoHasArray && instance->arrayDataLen <= len) { - for (int i = ctx->argumentCount - 1; i >= 0; --i) { - Value v = ctx->argument(i); - - if (!instance->sparseArray) { - if (!instance->arrayOffset) - instance->getArrayHeadRoom(); - - --instance->arrayOffset; - --instance->arrayData; - ++instance->arrayDataLen; - if (instance->arrayAttributes) { - --instance->arrayAttributes; - *instance->arrayAttributes = Attr_Data; - } - instance->arrayData->value = v; - } else { - uint idx = instance->allocArrayValue(v); - instance->sparseArray->push_front(idx); - } - } - } else { - for (uint k = len; k > 0; --k) { - bool exists; - Value v = instance->getIndexed(k - 1, &exists); - if (exists) - instance->putIndexed(k + ctx->argumentCount - 1, v); - else - instance->deleteIndexedProperty(k + ctx->argumentCount - 1); - } - for (uint i = 0; i < ctx->argumentCount; ++i) - instance->putIndexed(i, ctx->argument(i)); - } - - uint newLen = len + ctx->argumentCount; - if (instance->isArrayObject()) - instance->setArrayLengthUnchecked(newLen); - else - instance->put(ctx->engine->id_length, Value::fromDouble(newLen)); - - if (newLen < INT_MAX) - return Value::fromInt32(newLen); - return Value::fromDouble((double)newLen); -} - -Value ArrayPrototype::method_indexOf(SimpleCallContext *ctx) -{ - Object *instance = ctx->thisObject.toObject(ctx); - uint len = getLength(ctx, instance); - if (!len) - return Value::fromInt32(-1); - - Value searchValue; - uint fromIndex = 0; - - if (ctx->argumentCount >= 1) - searchValue = ctx->argument(0); - else - searchValue = Value::undefinedValue(); - - if (ctx->argumentCount >= 2) { - double f = ctx->argument(1).toInteger(); - if (f >= len) - return Value::fromInt32(-1); - if (f < 0) - f = qMax(len + f, 0.); - fromIndex = (uint) f; - } - - if (instance->isStringObject()) { - for (uint k = fromIndex; k < len; ++k) { - bool exists; - Value v = instance->getIndexed(k, &exists); - if (exists && __qmljs_strict_equal(v, searchValue)) - return Value::fromDouble(k); - } - return Value::fromInt32(-1); - } - - return instance->arrayIndexOf(searchValue, fromIndex, len, ctx, instance); -} - -Value ArrayPrototype::method_lastIndexOf(SimpleCallContext *ctx) -{ - Object *instance = ctx->thisObject.toObject(ctx); - uint len = getLength(ctx, instance); - if (!len) - return Value::fromInt32(-1); - - Value searchValue; - uint fromIndex = len; - - if (ctx->argumentCount >= 1) - searchValue = ctx->argument(0); - else - searchValue = Value::undefinedValue(); - - if (ctx->argumentCount >= 2) { - double f = ctx->argument(1).toInteger(); - if (f > 0) - f = qMin(f, (double)(len - 1)); - else if (f < 0) { - f = len + f; - if (f < 0) - return Value::fromInt32(-1); - } - fromIndex = (uint) f + 1; - } - - for (uint k = fromIndex; k > 0;) { - --k; - bool exists; - Value v = instance->getIndexed(k, &exists); - if (exists && __qmljs_strict_equal(v, searchValue)) - return Value::fromDouble(k); - } - return Value::fromInt32(-1); -} - -Value ArrayPrototype::method_every(SimpleCallContext *ctx) -{ - Object *instance = ctx->thisObject.toObject(ctx); - - uint len = getLength(ctx, instance); - - FunctionObject *callback = ctx->argument(0).asFunctionObject(); - if (!callback) - ctx->throwTypeError(); - - Value thisArg = ctx->argument(1); - - bool ok = true; - for (uint k = 0; ok && k < len; ++k) { - bool exists; - Value v = instance->getIndexed(k, &exists); - if (!exists) - continue; - - Value args[3]; - args[0] = v; - args[1] = Value::fromDouble(k); - args[2] = ctx->thisObject; - Value r = callback->call(thisArg, args, 3); - ok = r.toBoolean(); - } - return Value::fromBoolean(ok); -} - -Value ArrayPrototype::method_some(SimpleCallContext *ctx) -{ - Object *instance = ctx->thisObject.toObject(ctx); - - uint len = getLength(ctx, instance); - - FunctionObject *callback = ctx->argument(0).asFunctionObject(); - if (!callback) - ctx->throwTypeError(); - - Value thisArg = ctx->argument(1); - - for (uint k = 0; k < len; ++k) { - bool exists; - Value v = instance->getIndexed(k, &exists); - if (!exists) - continue; - - Value args[3]; - args[0] = v; - args[1] = Value::fromDouble(k); - args[2] = ctx->thisObject; - Value r = callback->call(thisArg, args, 3); - if (r.toBoolean()) - return Value::fromBoolean(true); - } - return Value::fromBoolean(false); -} - -Value ArrayPrototype::method_forEach(SimpleCallContext *ctx) -{ - Object *instance = ctx->thisObject.toObject(ctx); - - uint len = getLength(ctx, instance); - - FunctionObject *callback = ctx->argument(0).asFunctionObject(); - if (!callback) - ctx->throwTypeError(); - - Value thisArg = ctx->argument(1); - - for (uint k = 0; k < len; ++k) { - bool exists; - Value v = instance->getIndexed(k, &exists); - if (!exists) - continue; - - Value args[3]; - args[0] = v; - args[1] = Value::fromDouble(k); - args[2] = ctx->thisObject; - callback->call(thisArg, args, 3); - } - return Value::undefinedValue(); -} - -Value ArrayPrototype::method_map(SimpleCallContext *ctx) -{ - Object *instance = ctx->thisObject.toObject(ctx); - - uint len = getLength(ctx, instance); - - FunctionObject *callback = ctx->argument(0).asFunctionObject(); - if (!callback) - ctx->throwTypeError(); - - Value thisArg = ctx->argument(1); - - ArrayObject *a = ctx->engine->newArrayObject(); - a->arrayReserve(len); - a->setArrayLengthUnchecked(len); - - for (uint k = 0; k < len; ++k) { - bool exists; - Value v = instance->getIndexed(k, &exists); - if (!exists) - continue; - - Value args[3]; - args[0] = v; - args[1] = Value::fromDouble(k); - args[2] = ctx->thisObject; - Value mapped = callback->call(thisArg, args, 3); - a->arraySet(k, mapped); - } - return Value::fromObject(a); -} - -Value ArrayPrototype::method_filter(SimpleCallContext *ctx) -{ - Object *instance = ctx->thisObject.toObject(ctx); - - uint len = getLength(ctx, instance); - - FunctionObject *callback = ctx->argument(0).asFunctionObject(); - if (!callback) - ctx->throwTypeError(); - - Value thisArg = ctx->argument(1); - - ArrayObject *a = ctx->engine->newArrayObject(); - a->arrayReserve(len); - - uint to = 0; - for (uint k = 0; k < len; ++k) { - bool exists; - Value v = instance->getIndexed(k, &exists); - if (!exists) - continue; - - Value args[3]; - args[0] = v; - args[1] = Value::fromDouble(k); - args[2] = ctx->thisObject; - Value selected = callback->call(thisArg, args, 3); - if (selected.toBoolean()) { - a->arraySet(to, v); - ++to; - } - } - return Value::fromObject(a); -} - -Value ArrayPrototype::method_reduce(SimpleCallContext *ctx) -{ - Object *instance = ctx->thisObject.toObject(ctx); - - uint len = getLength(ctx, instance); - - FunctionObject *callback = ctx->argument(0).asFunctionObject(); - if (!callback) - ctx->throwTypeError(); - - uint k = 0; - Value acc; - if (ctx->argumentCount > 1) { - acc = ctx->argument(1); - } else { - bool kPresent = false; - while (k < len && !kPresent) { - Value v = instance->getIndexed(k, &kPresent); - if (kPresent) - acc = v; - ++k; - } - if (!kPresent) - ctx->throwTypeError(); - } - - while (k < len) { - bool kPresent; - Value v = instance->getIndexed(k, &kPresent); - if (kPresent) { - Value args[4]; - args[0] = acc; - args[1] = v; - args[2] = Value::fromDouble(k); - args[3] = ctx->thisObject; - acc = callback->call(Value::undefinedValue(), args, 4); - } - ++k; - } - return acc; -} - -Value ArrayPrototype::method_reduceRight(SimpleCallContext *ctx) -{ - Object *instance = ctx->thisObject.toObject(ctx); - - uint len = getLength(ctx, instance); - - FunctionObject *callback = ctx->argument(0).asFunctionObject(); - if (!callback) - ctx->throwTypeError(); - - if (len == 0) { - if (ctx->argumentCount == 1) - ctx->throwTypeError(); - return ctx->argument(1); - } - - uint k = len; - Value acc; - if (ctx->argumentCount > 1) { - acc = ctx->argument(1); - } else { - bool kPresent = false; - while (k > 0 && !kPresent) { - Value v = instance->getIndexed(k - 1, &kPresent); - if (kPresent) - acc = v; - --k; - } - if (!kPresent) - ctx->throwTypeError(); - } - - while (k > 0) { - bool kPresent; - Value v = instance->getIndexed(k - 1, &kPresent); - if (kPresent) { - Value args[4]; - args[0] = acc; - args[1] = v; - args[2] = Value::fromDouble(k - 1); - args[3] = ctx->thisObject; - acc = callback->call(Value::undefinedValue(), args, 4); - } - --k; - } - return acc; -} - diff --git a/src/qml/qml/v4/qv4arrayobject_p.h b/src/qml/qml/v4/qv4arrayobject_p.h deleted file mode 100644 index 13c3882f4f..0000000000 --- a/src/qml/qml/v4/qv4arrayobject_p.h +++ /dev/null @@ -1,100 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QV4ARRAYOBJECT_H -#define QV4ARRAYOBJECT_H - -#include "qv4object_p.h" -#include "qv4functionobject_p.h" -#include - -QT_BEGIN_NAMESPACE - -namespace QV4 { - -struct ArrayCtor: FunctionObject -{ - ArrayCtor(ExecutionContext *scope); - - static Value construct(Managed *m, Value *args, int argc); - static Value call(Managed *that, const Value &, Value *, int); - -protected: - static const ManagedVTable static_vtbl; -}; - -struct ArrayPrototype: ArrayObject -{ - ArrayPrototype(ExecutionContext *context); - - void init(ExecutionContext *ctx, const Value &ctor); - - static uint getLength(ExecutionContext *ctx, Object *o); - - static Value method_isArray(SimpleCallContext *ctx); - static Value method_toString(SimpleCallContext *ctx); - static Value method_toLocaleString(SimpleCallContext *ctx); - static Value method_concat(SimpleCallContext *ctx); - static Value method_join(SimpleCallContext *ctx); - static Value method_pop(SimpleCallContext *ctx); - static Value method_push(SimpleCallContext *ctx); - static Value method_reverse(SimpleCallContext *ctx); - static Value method_shift(SimpleCallContext *ctx); - static Value method_slice(SimpleCallContext *ctx); - static Value method_sort(SimpleCallContext *ctx); - static Value method_splice(SimpleCallContext *ctx); - static Value method_unshift(SimpleCallContext *ctx); - static Value method_indexOf(SimpleCallContext *ctx); - static Value method_lastIndexOf(SimpleCallContext *ctx); - static Value method_every(SimpleCallContext *ctx); - static Value method_some(SimpleCallContext *ctx); - static Value method_forEach(SimpleCallContext *ctx); - static Value method_map(SimpleCallContext *ctx); - static Value method_filter(SimpleCallContext *ctx); - static Value method_reduce(SimpleCallContext *ctx); - static Value method_reduceRight(SimpleCallContext *ctx); -}; - - -} // namespace QV4 - -QT_END_NAMESPACE - -#endif // QV4ECMAOBJECTS_P_H diff --git a/src/qml/qml/v4/qv4booleanobject.cpp b/src/qml/qml/v4/qv4booleanobject.cpp deleted file mode 100644 index 24678d23dc..0000000000 --- a/src/qml/qml/v4/qv4booleanobject.cpp +++ /dev/null @@ -1,97 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4booleanobject_p.h" - -using namespace QV4; - -DEFINE_MANAGED_VTABLE(BooleanCtor); - -BooleanCtor::BooleanCtor(ExecutionContext *scope) - : FunctionObject(scope, scope->engine->newIdentifier("Boolean")) -{ - vtbl = &static_vtbl; -} - -Value BooleanCtor::construct(Managed *m, Value *args, int argc) -{ - bool n = argc ? args[0].toBoolean() : false; - return Value::fromObject(m->engine()->newBooleanObject(Value::fromBoolean(n))); -} - -Value BooleanCtor::call(Managed *, const Value &, Value *argv, int argc) -{ - bool value = argc ? argv[0].toBoolean() : 0; - return Value::fromBoolean(value); -} - -void BooleanPrototype::init(ExecutionContext *ctx, const Value &ctor) -{ - ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); - ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); - defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); - defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString); - defineDefaultProperty(ctx, QStringLiteral("valueOf"), method_valueOf); -} - -Value BooleanPrototype::method_toString(SimpleCallContext *ctx) -{ - bool result; - if (ctx->thisObject.isBoolean()) { - result = ctx->thisObject.booleanValue(); - } else { - BooleanObject *thisObject = ctx->thisObject.asBooleanObject(); - if (!thisObject) - ctx->throwTypeError(); - result = thisObject->value.booleanValue(); - } - - return Value::fromString(ctx, QLatin1String(result ? "true" : "false")); -} - -Value BooleanPrototype::method_valueOf(SimpleCallContext *ctx) -{ - BooleanObject *thisObject = ctx->thisObject.asBooleanObject(); - if (!thisObject) - ctx->throwTypeError(); - - return thisObject->value; -} diff --git a/src/qml/qml/v4/qv4booleanobject_p.h b/src/qml/qml/v4/qv4booleanobject_p.h deleted file mode 100644 index 3e5e7663f2..0000000000 --- a/src/qml/qml/v4/qv4booleanobject_p.h +++ /dev/null @@ -1,77 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QV4BOOLEANOBJECT_H -#define QBOOLEANOBJECT_H - -#include "qv4object_p.h" -#include "qv4functionobject_p.h" -#include - -QT_BEGIN_NAMESPACE - -namespace QV4 { - -struct BooleanCtor: FunctionObject -{ - BooleanCtor(ExecutionContext *scope); - - static Value construct(Managed *, Value *args, int argc); - static Value call(Managed *that, const Value &, Value *, int); - -protected: - static const ManagedVTable static_vtbl; -}; - -struct BooleanPrototype: BooleanObject -{ - BooleanPrototype(ExecutionEngine *engine): BooleanObject(engine, Value::fromBoolean(false)) {} - void init(ExecutionContext *ctx, const Value &ctor); - - static Value method_toString(SimpleCallContext *ctx); - static Value method_valueOf(SimpleCallContext *ctx); -}; - - -} - -QT_END_NAMESPACE - -#endif // QV4ECMAOBJECTS_P_H diff --git a/src/qml/qml/v4/qv4codegen.cpp b/src/qml/qml/v4/qv4codegen.cpp deleted file mode 100644 index d0c43c8f56..0000000000 --- a/src/qml/qml/v4/qv4codegen.cpp +++ /dev/null @@ -1,2605 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4codegen_p.h" -#include "qv4util_p.h" -#include "qv4debugging_p.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef CONST -#undef CONST -#endif - -#define QV4_NO_LIVENESS -#undef SHOW_SSA - -using namespace QQmlJS; -using namespace AST; - -class Codegen::ScanFunctions: Visitor -{ - typedef QV4::TemporaryAssignment TemporaryBoolAssignment; -public: - ScanFunctions(Codegen *cg, const QString &sourceCode) - : _cg(cg) - , _sourceCode(sourceCode) - , _env(0) - , _inFuncBody(false) - , _allowFuncDecls(true) - { - } - - void operator()(Node *node) - { - if (node) - node->accept(this); - } - - inline void enterEnvironment(Node *node) - { - Environment *e = _cg->newEnvironment(node, _env); - if (!e->isStrict) - e->isStrict = _cg->_strictMode; - _envStack.append(e); - _env = e; - } - - inline void leaveEnvironment() - { - _envStack.pop(); - _env = _envStack.isEmpty() ? 0 : _envStack.top(); - } - -protected: - using Visitor::visit; - using Visitor::endVisit; - - void checkDirectivePrologue(SourceElements *ast) - { - for (SourceElements *it = ast; it; it = it->next) { - if (StatementSourceElement *stmt = cast(it->element)) { - if (ExpressionStatement *expr = cast(stmt->statement)) { - if (StringLiteral *strLit = cast(expr->expression)) { - // Use the source code, because the StringLiteral's - // value might have escape sequences in it, which is not - // allowed. - if (strLit->literalToken.length < 2) - continue; - QStringRef str = _sourceCode.midRef(strLit->literalToken.offset + 1, strLit->literalToken.length - 2); - if (str == QStringLiteral("use strict")) { - _env->isStrict = true; - } else { - // TODO: give a warning. - } - continue; - } - } - } - - break; - } - } - - void checkName(const QStringRef &name, const SourceLocation &loc) - { - if (_env->isStrict) { - if (name == QLatin1String("implements") - || name == QLatin1String("interface") - || name == QLatin1String("let") - || name == QLatin1String("package") - || name == QLatin1String("private") - || name == QLatin1String("protected") - || name == QLatin1String("public") - || name == QLatin1String("static") - || name == QLatin1String("yield")) { - _cg->throwSyntaxError(loc, QCoreApplication::translate("qv4codegen", "Unexpected strict mode reserved word")); - } - } - } - void checkForArguments(AST::FormalParameterList *parameters) - { - while (parameters) { - if (parameters->name == QStringLiteral("arguments")) - _env->usesArgumentsObject = Environment::ArgumentsObjectNotUsed; - parameters = parameters->next; - } - } - - virtual bool visit(Program *ast) - { - enterEnvironment(ast); - checkDirectivePrologue(ast->elements); - return true; - } - - virtual void endVisit(Program *) - { - leaveEnvironment(); - } - - virtual bool visit(CallExpression *ast) - { - if (! _env->hasDirectEval) { - if (IdentifierExpression *id = cast(ast->base)) { - if (id->name == QStringLiteral("eval")) { - if (_env->usesArgumentsObject == Environment::ArgumentsObjectUnknown) - _env->usesArgumentsObject = Environment::ArgumentsObjectUsed; - _env->hasDirectEval = true; - } - } - } - int argc = 0; - for (ArgumentList *it = ast->arguments; it; it = it->next) - ++argc; - _env->maxNumberOfArguments = qMax(_env->maxNumberOfArguments, argc); - return true; - } - - virtual bool visit(NewMemberExpression *ast) - { - int argc = 0; - for (ArgumentList *it = ast->arguments; it; it = it->next) - ++argc; - _env->maxNumberOfArguments = qMax(_env->maxNumberOfArguments, argc); - return true; - } - - virtual bool visit(ArrayLiteral *ast) - { - int index = 0; - for (ElementList *it = ast->elements; it; it = it->next) { - for (Elision *elision = it->elision; elision; elision = elision->next) - ++index; - ++index; - } - if (ast->elision) { - for (Elision *elision = ast->elision->next; elision; elision = elision->next) - ++index; - } - _env->maxNumberOfArguments = qMax(_env->maxNumberOfArguments, index); - return true; - } - - virtual bool visit(VariableDeclaration *ast) - { - if (_env->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments"))) - _cg->throwSyntaxError(ast->identifierToken, QCoreApplication::translate("qv4codegen", "Variable name may not be eval or arguments in strict mode")); - checkName(ast->name, ast->identifierToken); - if (ast->name == QLatin1String("arguments")) - _env->usesArgumentsObject = Environment::ArgumentsObjectNotUsed; - _env->enter(ast->name.toString(), ast->expression ? Environment::VariableDefinition : Environment::VariableDeclaration); - return true; - } - - virtual bool visit(IdentifierExpression *ast) - { - checkName(ast->name, ast->identifierToken); - if (_env->usesArgumentsObject == Environment::ArgumentsObjectUnknown && ast->name == QLatin1String("arguments")) - _env->usesArgumentsObject = Environment::ArgumentsObjectUsed; - return true; - } - - virtual bool visit(ExpressionStatement *ast) - { - if (FunctionExpression* expr = AST::cast(ast->expression)) { - if (!_allowFuncDecls) - _cg->throwSyntaxError(expr->functionToken, QCoreApplication::translate("qv4codegen", "conditional function or closure declaration")); - - enterFunction(expr, /*enterName*/ true); - Node::accept(expr->formals, this); - Node::accept(expr->body, this); - leaveEnvironment(); - return false; - } else { - SourceLocation firstToken = ast->firstSourceLocation(); - if (_sourceCode.midRef(firstToken.offset, firstToken.length) == QStringLiteral("function")) { - _cg->throwSyntaxError(firstToken, QCoreApplication::translate("qv4codegen", "unexpected token")); - } - } - return true; - } - - virtual bool visit(FunctionExpression *ast) - { - enterFunction(ast, /*enterName*/ false); - return true; - } - - void enterFunction(FunctionExpression *ast, bool enterName, bool isExpression = true) - { - if (_env->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments"))) - _cg->throwSyntaxError(ast->identifierToken, QCoreApplication::translate("qv4codegen", "Function name may not be eval or arguments in strict mode")); - enterFunction(ast, ast->name.toString(), ast->formals, ast->body, enterName ? ast : 0, isExpression); - } - - virtual void endVisit(FunctionExpression *) - { - leaveEnvironment(); - } - - virtual bool visit(ObjectLiteral *ast) - { - int argc = 0; - for (PropertyAssignmentList *it = ast->properties; it; it = it->next) { - ++argc; - if (AST::cast(it->assignment)) - ++argc; - } - _env->maxNumberOfArguments = qMax(_env->maxNumberOfArguments, argc); - - TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, true); - Node::accept(ast->properties, this); - return false; - } - - virtual bool visit(PropertyGetterSetter *ast) - { - TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, true); - enterFunction(ast, QString(), ast->formals, ast->functionBody, /*FunctionExpression*/0, /*isExpression*/false); - return true; - } - - virtual void endVisit(PropertyGetterSetter *) - { - leaveEnvironment(); - } - - virtual bool visit(FunctionDeclaration *ast) - { - enterFunction(ast, /*enterName*/ true, /*isExpression */false); - return true; - } - - virtual void endVisit(FunctionDeclaration *) - { - leaveEnvironment(); - } - - virtual bool visit(FunctionBody *ast) - { - TemporaryBoolAssignment inFuncBody(_inFuncBody, true); - Node::accept(ast->elements, this); - return false; - } - - virtual bool visit(WithStatement *ast) - { - if (_env->isStrict) { - _cg->throwSyntaxError(ast->withToken, QCoreApplication::translate("qv4codegen", "'with' statement is not allowed in strict mode")); - return false; - } - - return true; - } - - virtual bool visit(IfStatement *ast) { - Node::accept(ast->expression, this); - - TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_inFuncBody); - Node::accept(ast->ok, this); - Node::accept(ast->ko, this); - - return false; - } - - virtual bool visit(WhileStatement *ast) { - Node::accept(ast->expression, this); - - TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_inFuncBody); - Node::accept(ast->statement, this); - - return false; - } - - virtual bool visit(DoWhileStatement *ast) { - { - TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_env->isStrict); - Node::accept(ast->statement, this); - } - Node::accept(ast->expression, this); - return false; - } - - virtual bool visit(ForStatement *ast) { - Node::accept(ast->initialiser, this); - Node::accept(ast->condition, this); - Node::accept(ast->expression, this); - - TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_env->isStrict); - Node::accept(ast->statement, this); - - return false; - } - - virtual bool visit(LocalForStatement *ast) { - Node::accept(ast->declarations, this); - Node::accept(ast->condition, this); - Node::accept(ast->expression, this); - - TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_env->isStrict); - Node::accept(ast->statement, this); - - return false; - } - - virtual bool visit(ForEachStatement *ast) { - Node::accept(ast->initialiser, this); - Node::accept(ast->expression, this); - - TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_env->isStrict); - Node::accept(ast->statement, this); - - return false; - } - - virtual bool visit(LocalForEachStatement *ast) { - Node::accept(ast->declaration, this); - Node::accept(ast->expression, this); - - TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_env->isStrict); - Node::accept(ast->statement, this); - - return false; - } - - virtual bool visit(Block *ast) { - TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, _env->isStrict ? false : _allowFuncDecls); - Node::accept(ast->statements, this); - return false; - } - -private: - void enterFunction(Node *ast, const QString &name, FormalParameterList *formals, FunctionBody *body, FunctionExpression *expr, bool isExpression) - { - bool wasStrict = false; - if (_env) { - _env->hasNestedFunctions = true; - // The identifier of a function expression cannot be referenced from the enclosing environment. - if (expr) - _env->enter(name, Environment::FunctionDefinition, expr); - if (name == QLatin1String("arguments")) - _env->usesArgumentsObject = Environment::ArgumentsObjectNotUsed; - wasStrict = _env->isStrict; - } - - enterEnvironment(ast); - checkForArguments(formals); - - _env->isNamedFunctionExpression = isExpression && !name.isEmpty(); - _env->formals = formals; - - if (body) - checkDirectivePrologue(body->elements); - - if (wasStrict || _env->isStrict) { - QStringList args; - for (FormalParameterList *it = formals; it; it = it->next) { - QString arg = it->name.toString(); - if (args.contains(arg)) - _cg->throwSyntaxError(it->identifierToken, QCoreApplication::translate("qv4codegen", "Duplicate parameter name '%1' is not allowed in strict mode").arg(arg)); - if (arg == QLatin1String("eval") || arg == QLatin1String("arguments")) - _cg->throwSyntaxError(it->identifierToken, QCoreApplication::translate("qv4codegen", "'%1' cannot be used as parameter name in strict mode").arg(arg)); - args += arg; - } - } - } - -private: // fields: - Codegen *_cg; - const QString _sourceCode; - Environment *_env; - QStack _envStack; - - bool _inFuncBody; - bool _allowFuncDecls; -}; - -Codegen::Codegen(QV4::ExecutionContext *context, bool strict) - : _module(0) - , _function(0) - , _block(0) - , _exitBlock(0) - , _throwBlock(0) - , _returnAddress(0) - , _mode(GlobalCode) - , _env(0) - , _loop(0) - , _labelledStatement(0) - , _scopeAndFinally(0) - , _context(context) - , _strictMode(strict) - , _errorHandler(0) -{ -} - -Codegen::Codegen(ErrorHandler *errorHandler, bool strictMode) - : _module(0) - , _function(0) - , _block(0) - , _exitBlock(0) - , _throwBlock(0) - , _returnAddress(0) - , _mode(GlobalCode) - , _env(0) - , _loop(0) - , _labelledStatement(0) - , _scopeAndFinally(0) - , _context(0) - , _strictMode(strictMode) - , _errorHandler(errorHandler) -{ -} - -V4IR::Function *Codegen::operator()(const QString &fileName, - const QString &sourceCode, - Program *node, - V4IR::Module *module, - Mode mode, - const QStringList &inheritedLocals) -{ - assert(node); - - _fileName = fileName; - _module = module; - _env = 0; - - ScanFunctions scan(this, sourceCode); - scan(node); - - V4IR::Function *globalCode = defineFunction(QStringLiteral("%entry"), node, 0, - node->elements, mode, inheritedLocals); - qDeleteAll(_envMap); - _envMap.clear(); - - return globalCode; -} - -V4IR::Function *Codegen::operator()(const QString &fileName, - const QString &sourceCode, - AST::FunctionExpression *ast, - V4IR::Module *module) -{ - _fileName = fileName; - _module = module; - _env = 0; - - ScanFunctions scan(this, sourceCode); - // fake a global environment - scan.enterEnvironment(0); - scan(ast); - scan.leaveEnvironment(); - - V4IR::Function *function = defineFunction(ast->name.toString(), ast, ast->formals, ast->body ? ast->body->elements : 0); - - qDeleteAll(_envMap); - _envMap.clear(); - - return function; -} - - -void Codegen::enterEnvironment(Node *node) -{ - _env = _envMap.value(node); - assert(_env); -} - -void Codegen::leaveEnvironment() -{ - assert(_env); - _env = _env->parent; -} - -void Codegen::enterLoop(Statement *node, V4IR::BasicBlock *startBlock, V4IR::BasicBlock *breakBlock, V4IR::BasicBlock *continueBlock) -{ - if (startBlock) - startBlock->markAsGroupStart(); - _loop = new Loop(node, startBlock, breakBlock, continueBlock, _loop); - _loop->labelledStatement = _labelledStatement; // consume the enclosing labelled statement - _loop->scopeAndFinally = _scopeAndFinally; - _labelledStatement = 0; -} - -void Codegen::leaveLoop() -{ - Loop *current = _loop; - _loop = _loop->parent; - delete current; -} - -V4IR::Expr *Codegen::member(V4IR::Expr *base, const QString *name) -{ - if (base->asTemp() /*|| base->asName()*/) - return _block->MEMBER(base->asTemp(), name); - else { - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), base); - return _block->MEMBER(_block->TEMP(t), name); - } -} - -V4IR::Expr *Codegen::subscript(V4IR::Expr *base, V4IR::Expr *index) -{ - if (! base->asTemp()) { - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), base); - base = _block->TEMP(t); - } - - if (! index->asTemp()) { - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), index); - index = _block->TEMP(t); - } - - assert(base->asTemp() && index->asTemp()); - return _block->SUBSCRIPT(base->asTemp(), index->asTemp()); -} - -V4IR::Expr *Codegen::argument(V4IR::Expr *expr) -{ - if (expr && ! expr->asTemp()) { - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), expr); - expr = _block->TEMP(t); - } - return expr; -} - -// keeps references alive, converts other expressions to temps -V4IR::Expr *Codegen::reference(V4IR::Expr *expr) -{ - if (expr && !expr->asTemp() && !expr->asName() && !expr->asMember() && !expr->asSubscript()) { - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), expr); - expr = _block->TEMP(t); - } - return expr; -} - -V4IR::Expr *Codegen::unop(V4IR::AluOp op, V4IR::Expr *expr) -{ - Q_ASSERT(op != V4IR::OpIncrement); - Q_ASSERT(op != V4IR::OpDecrement); - - if (V4IR::Const *c = expr->asConst()) { - if (c->type == V4IR::NumberType) { - switch (op) { - case V4IR::OpNot: - return _block->CONST(V4IR::BoolType, !c->value); - case V4IR::OpUMinus: - return _block->CONST(V4IR::NumberType, -c->value); - case V4IR::OpUPlus: - return expr; - case V4IR::OpCompl: - return _block->CONST(V4IR::NumberType, ~QV4::Value::toInt32(c->value)); - case V4IR::OpIncrement: - return _block->CONST(V4IR::NumberType, c->value + 1); - case V4IR::OpDecrement: - return _block->CONST(V4IR::NumberType, c->value - 1); - default: - break; - } - } - } - if (! expr->asTemp()) { - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), expr); - expr = _block->TEMP(t); - } - assert(expr->asTemp()); - return _block->UNOP(op, expr->asTemp()); -} - -V4IR::Expr *Codegen::binop(V4IR::AluOp op, V4IR::Expr *left, V4IR::Expr *right) -{ - if (V4IR::Const *c1 = left->asConst()) { - if (V4IR::Const *c2 = right->asConst()) { - if (c1->type == V4IR::NumberType && c2->type == V4IR::NumberType) { - switch (op) { - case V4IR::OpAdd: return _block->CONST(V4IR::NumberType, c1->value + c2->value); - case V4IR::OpAnd: return _block->CONST(V4IR::BoolType, c1->value ? c2->value : 0); - case V4IR::OpBitAnd: return _block->CONST(V4IR::NumberType, int(c1->value) & int(c2->value)); - case V4IR::OpBitOr: return _block->CONST(V4IR::NumberType, int(c1->value) | int(c2->value)); - case V4IR::OpBitXor: return _block->CONST(V4IR::NumberType, int(c1->value) ^ int(c2->value)); - case V4IR::OpDiv: return _block->CONST(V4IR::NumberType, c1->value / c2->value); - case V4IR::OpEqual: return _block->CONST(V4IR::BoolType, c1->value == c2->value); - case V4IR::OpNotEqual: return _block->CONST(V4IR::BoolType, c1->value != c2->value); - case V4IR::OpStrictEqual: return _block->CONST(V4IR::BoolType, c1->value == c2->value); - case V4IR::OpStrictNotEqual: return _block->CONST(V4IR::BoolType, c1->value != c2->value); - case V4IR::OpGe: return _block->CONST(V4IR::BoolType, c1->value >= c2->value); - case V4IR::OpGt: return _block->CONST(V4IR::BoolType, c1->value > c2->value); - case V4IR::OpLe: return _block->CONST(V4IR::BoolType, c1->value <= c2->value); - case V4IR::OpLt: return _block->CONST(V4IR::BoolType, c1->value < c2->value); - case V4IR::OpLShift: return _block->CONST(V4IR::NumberType, QV4::Value::toInt32(c1->value) << (QV4::Value::toUInt32(c2->value) & 0x1f)); - case V4IR::OpMod: return _block->CONST(V4IR::NumberType, std::fmod(c1->value, c2->value)); - case V4IR::OpMul: return _block->CONST(V4IR::NumberType, c1->value * c2->value); - case V4IR::OpOr: return _block->CONST(V4IR::NumberType, c1->value ? c1->value : c2->value); - case V4IR::OpRShift: return _block->CONST(V4IR::NumberType, QV4::Value::toInt32(c1->value) >> (QV4::Value::toUInt32(c2->value) & 0x1f)); - case V4IR::OpSub: return _block->CONST(V4IR::NumberType, c1->value - c2->value); - case V4IR::OpURShift: return _block->CONST(V4IR::NumberType,QV4::Value::toUInt32(c1->value) >> (QV4::Value::toUInt32(c2->value) & 0x1f)); - - case V4IR::OpInstanceof: - case V4IR::OpIn: - break; - - case V4IR::OpIfTrue: // unary ops - case V4IR::OpNot: - case V4IR::OpUMinus: - case V4IR::OpUPlus: - case V4IR::OpCompl: - case V4IR::OpIncrement: - case V4IR::OpDecrement: - case V4IR::OpInvalid: - break; - } - } - } - } else if (op == V4IR::OpAdd) { - if (V4IR::String *s1 = left->asString()) { - if (V4IR::String *s2 = right->asString()) { - return _block->STRING(_function->newString(*s1->value + *s2->value)); - } - } - } - - if (!left->asTemp()) { - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), left); - left = _block->TEMP(t); - } - - if (!right->asTemp()) { - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), right); - right = _block->TEMP(t); - } - - assert(left->asTemp()); - assert(right->asTemp()); - - return _block->BINOP(op, left, right); -} - -V4IR::Expr *Codegen::call(V4IR::Expr *base, V4IR::ExprList *args) -{ - base = reference(base); - return _block->CALL(base, args); -} - -void Codegen::move(V4IR::Expr *target, V4IR::Expr *source, V4IR::AluOp op) -{ - assert(target->isLValue()); - - // TODO: verify the rest of the function for when op == OpInvalid - if (op != V4IR::OpInvalid) { - move(target, binop(op, target, source), V4IR::OpInvalid); - return; - } - - if (!source->asTemp() && !source->asConst() && (op != V4IR::OpInvalid || ! target->asTemp())) { - unsigned t = _block->newTemp(); - _block->MOVE(_block->TEMP(t), source); - source = _block->TEMP(t); - } - if (source->asConst() && (!target->asTemp() || op != V4IR::OpInvalid)) { - unsigned t = _block->newTemp(); - _block->MOVE(_block->TEMP(t), source); - source = _block->TEMP(t); - } - - _block->MOVE(target, source, op); -} - -void Codegen::cjump(V4IR::Expr *cond, V4IR::BasicBlock *iftrue, V4IR::BasicBlock *iffalse) -{ - if (! (cond->asTemp() || cond->asBinop())) { - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), cond); - cond = _block->TEMP(t); - } - _block->CJUMP(cond, iftrue, iffalse); -} - -void Codegen::accept(Node *node) -{ - if (node) - node->accept(this); -} - -void Codegen::statement(Statement *ast) -{ - _block->nextLocation = ast->firstSourceLocation(); - accept(ast); -} - -void Codegen::statement(ExpressionNode *ast) -{ - if (! ast) { - return; - } else { - Result r(nx); - qSwap(_expr, r); - accept(ast); - qSwap(_expr, r); - if (r.format == ex) { - if (r->asCall()) { - _block->EXP(*r); // the nest nx representation for calls is EXP(CALL(c..)) - } else if (r->asTemp()) { - // there is nothing to do - } else { - unsigned t = _block->newTemp(); - move(_block->TEMP(t), *r); - } - } - } -} - -void Codegen::condition(ExpressionNode *ast, V4IR::BasicBlock *iftrue, V4IR::BasicBlock *iffalse) -{ - if (ast) { - Result r(iftrue, iffalse); - qSwap(_expr, r); - accept(ast); - qSwap(_expr, r); - if (r.format == ex) { - cjump(*r, r.iftrue, r.iffalse); - } - } -} - -Codegen::Result Codegen::expression(ExpressionNode *ast) -{ - Result r; - if (ast) { - qSwap(_expr, r); - accept(ast); - qSwap(_expr, r); - } - return r; -} - -QString Codegen::propertyName(PropertyName *ast) -{ - QString p; - if (ast) { - qSwap(_property, p); - accept(ast); - qSwap(_property, p); - } - return p; -} - -Codegen::Result Codegen::sourceElement(SourceElement *ast) -{ - Result r(nx); - if (ast) { - qSwap(_expr, r); - accept(ast); - qSwap(_expr, r); - } - return r; -} - -Codegen::UiMember Codegen::uiObjectMember(UiObjectMember *ast) -{ - UiMember m; - if (ast) { - qSwap(_uiMember, m); - accept(ast); - qSwap(_uiMember, m); - } - return m; -} - -void Codegen::functionBody(FunctionBody *ast) -{ - if (ast) - sourceElements(ast->elements); -} - -void Codegen::program(Program *ast) -{ - if (ast) { - sourceElements(ast->elements); - } -} - -void Codegen::sourceElements(SourceElements *ast) -{ - for (SourceElements *it = ast; it; it = it->next) { - sourceElement(it->element); - } -} - -void Codegen::variableDeclaration(VariableDeclaration *ast) -{ - V4IR::Expr *initializer = 0; - if (!ast->expression) - return; - Result expr = expression(ast->expression); - assert(expr.code); - initializer = *expr; - - if (! _env->parent || _function->insideWithOrCatch) { - // it's global code. - move(_block->NAME(ast->name.toString(), ast->identifierToken.startLine, ast->identifierToken.startColumn), initializer); - } else { - const int index = _env->findMember(ast->name.toString()); - assert(index != -1); - move(_block->LOCAL(index, 0), initializer); - } -} - -void Codegen::variableDeclarationList(VariableDeclarationList *ast) -{ - for (VariableDeclarationList *it = ast; it; it = it->next) { - variableDeclaration(it->declaration); - } -} - - -bool Codegen::visit(ArgumentList *) -{ - assert(!"unreachable"); - return false; -} - -bool Codegen::visit(CaseBlock *) -{ - assert(!"unreachable"); - return false; -} - -bool Codegen::visit(CaseClause *) -{ - assert(!"unreachable"); - return false; -} - -bool Codegen::visit(CaseClauses *) -{ - assert(!"unreachable"); - return false; -} - -bool Codegen::visit(Catch *) -{ - assert(!"unreachable"); - return false; -} - -bool Codegen::visit(DefaultClause *) -{ - assert(!"unreachable"); - return false; -} - -bool Codegen::visit(ElementList *) -{ - assert(!"unreachable"); - return false; -} - -bool Codegen::visit(Elision *) -{ - assert(!"unreachable"); - return false; -} - -bool Codegen::visit(Finally *) -{ - assert(!"unreachable"); - return false; -} - -bool Codegen::visit(FormalParameterList *) -{ - assert(!"unreachable"); - return false; -} - -bool Codegen::visit(FunctionBody *) -{ - assert(!"unreachable"); - return false; -} - -bool Codegen::visit(Program *) -{ - assert(!"unreachable"); - return false; -} - -bool Codegen::visit(PropertyAssignmentList *) -{ - assert(!"unreachable"); - return false; -} - -bool Codegen::visit(PropertyNameAndValue *) -{ - assert(!"unreachable"); - return false; -} - -bool Codegen::visit(PropertyGetterSetter *) -{ - assert(!"unreachable"); - return false; -} - -bool Codegen::visit(SourceElements *) -{ - assert(!"unreachable"); - return false; -} - -bool Codegen::visit(StatementList *) -{ - assert(!"unreachable"); - return false; -} - -bool Codegen::visit(UiArrayMemberList *) -{ - assert(!"unreachable"); - return false; -} - -bool Codegen::visit(UiImport *) -{ - assert(!"unreachable"); - return false; -} - -bool Codegen::visit(UiImportList *) -{ - assert(!"unreachable"); - return false; -} - -bool Codegen::visit(UiObjectInitializer *) -{ - assert(!"unreachable"); - return false; -} - -bool Codegen::visit(UiObjectMemberList *) -{ - assert(!"unreachable"); - return false; -} - -bool Codegen::visit(UiParameterList *) -{ - assert(!"unreachable"); - return false; -} - -bool Codegen::visit(UiProgram *) -{ - assert(!"unreachable"); - return false; -} - -bool Codegen::visit(UiQualifiedId *) -{ - assert(!"unreachable"); - return false; -} - -bool Codegen::visit(VariableDeclaration *) -{ - assert(!"unreachable"); - return false; -} - -bool Codegen::visit(VariableDeclarationList *) -{ - assert(!"unreachable"); - return false; -} - -bool Codegen::visit(Expression *ast) -{ - statement(ast->left); - accept(ast->right); - return false; -} - -bool Codegen::visit(ArrayLiteral *ast) -{ - V4IR::ExprList *args = 0; - V4IR::ExprList *current = 0; - for (ElementList *it = ast->elements; it; it = it->next) { - for (Elision *elision = it->elision; elision; elision = elision->next) { - V4IR::ExprList *arg = _function->New(); - if (!current) { - args = arg; - } else { - current->next = arg; - } - current = arg; - current->expr = _block->CONST(V4IR::MissingType, 0); - } - Result expr = expression(it->expression); - - V4IR::ExprList *arg = _function->New(); - if (!current) { - args = arg; - } else { - current->next = arg; - } - current = arg; - - V4IR::Expr *exp = *expr; - if (exp->asTemp() || exp->asConst()) { - current->expr = exp; - } else { - unsigned value = _block->newTemp(); - move(_block->TEMP(value), exp); - current->expr = _block->TEMP(value); - } - } - for (Elision *elision = ast->elision; elision; elision = elision->next) { - V4IR::ExprList *arg = _function->New(); - if (!current) { - args = arg; - } else { - current->next = arg; - } - current = arg; - current->expr = _block->CONST(V4IR::MissingType, 0); - } - - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), _block->CALL(_block->NAME(V4IR::Name::builtin_define_array, 0, 0), args)); - _expr.code = _block->TEMP(t); - return false; -} - -bool Codegen::visit(ArrayMemberExpression *ast) -{ - Result base = expression(ast->base); - Result index = expression(ast->expression); - _expr.code = subscript(*base, *index); - return false; -} - -static V4IR::AluOp baseOp(int op) -{ - switch ((QSOperator::Op) op) { - case QSOperator::InplaceAnd: return V4IR::OpBitAnd; - case QSOperator::InplaceSub: return V4IR::OpSub; - case QSOperator::InplaceDiv: return V4IR::OpDiv; - case QSOperator::InplaceAdd: return V4IR::OpAdd; - case QSOperator::InplaceLeftShift: return V4IR::OpLShift; - case QSOperator::InplaceMod: return V4IR::OpMod; - case QSOperator::InplaceMul: return V4IR::OpMul; - case QSOperator::InplaceOr: return V4IR::OpBitOr; - case QSOperator::InplaceRightShift: return V4IR::OpRShift; - case QSOperator::InplaceURightShift: return V4IR::OpURShift; - case QSOperator::InplaceXor: return V4IR::OpBitXor; - default: return V4IR::OpInvalid; - } -} - -bool Codegen::visit(BinaryExpression *ast) -{ - if (ast->op == QSOperator::And) { - if (_expr.accept(cx)) { - V4IR::BasicBlock *iftrue = _function->newBasicBlock(groupStartBlock()); - condition(ast->left, iftrue, _expr.iffalse); - _block = iftrue; - condition(ast->right, _expr.iftrue, _expr.iffalse); - } else { - V4IR::BasicBlock *iftrue = _function->newBasicBlock(groupStartBlock()); - V4IR::BasicBlock *endif = _function->newBasicBlock(groupStartBlock()); - - const unsigned r = _block->newTemp(); - - move(_block->TEMP(r), *expression(ast->left)); - cjump(_block->TEMP(r), iftrue, endif); - _block = iftrue; - move(_block->TEMP(r), *expression(ast->right)); - _block->JUMP(endif); - - _expr.code = _block->TEMP(r); - _block = endif; - } - return false; - } else if (ast->op == QSOperator::Or) { - if (_expr.accept(cx)) { - V4IR::BasicBlock *iffalse = _function->newBasicBlock(groupStartBlock()); - condition(ast->left, _expr.iftrue, iffalse); - _block = iffalse; - condition(ast->right, _expr.iftrue, _expr.iffalse); - } else { - V4IR::BasicBlock *iffalse = _function->newBasicBlock(groupStartBlock()); - V4IR::BasicBlock *endif = _function->newBasicBlock(groupStartBlock()); - - const unsigned r = _block->newTemp(); - move(_block->TEMP(r), *expression(ast->left)); - cjump(_block->TEMP(r), endif, iffalse); - _block = iffalse; - move(_block->TEMP(r), *expression(ast->right)); - _block->JUMP(endif); - - _block = endif; - _expr.code = _block->TEMP(r); - } - return false; - } - - V4IR::Expr* left = *expression(ast->left); - throwSyntaxErrorOnEvalOrArgumentsInStrictMode(left, ast->left->lastSourceLocation()); - - switch (ast->op) { - case QSOperator::Or: - case QSOperator::And: - break; - - case QSOperator::Assign: { - V4IR::Expr* right = *expression(ast->right); - if (! (left->asTemp() || left->asName() || left->asSubscript() || left->asMember())) - throwReferenceError(ast->operatorToken, QCoreApplication::translate("qv4codegen", "left-hand side of assignment operator is not an lvalue")); - - if (_expr.accept(nx)) { - move(left, right); - } else { - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), right); - move(left, _block->TEMP(t)); - _expr.code = _block->TEMP(t); - } - break; - } - - case QSOperator::InplaceAnd: - case QSOperator::InplaceSub: - case QSOperator::InplaceDiv: - case QSOperator::InplaceAdd: - case QSOperator::InplaceLeftShift: - case QSOperator::InplaceMod: - case QSOperator::InplaceMul: - case QSOperator::InplaceOr: - case QSOperator::InplaceRightShift: - case QSOperator::InplaceURightShift: - case QSOperator::InplaceXor: { - V4IR::Expr* right = *expression(ast->right); - if (!left->isLValue()) - throwSyntaxError(ast->operatorToken, QCoreApplication::translate("qv4codegen", "left-hand side of inplace operator is not an lvalue")); - - if (_expr.accept(nx)) { - move(left, right, baseOp(ast->op)); - } else { - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), right); - move(left, _block->TEMP(t), baseOp(ast->op)); - _expr.code = left; - } - break; - } - - case QSOperator::In: - case QSOperator::InstanceOf: - case QSOperator::Equal: - case QSOperator::NotEqual: - case QSOperator::Ge: - case QSOperator::Gt: - case QSOperator::Le: - case QSOperator::Lt: - case QSOperator::StrictEqual: - case QSOperator::StrictNotEqual: { - if (!left->asTemp() && !left->asConst()) { - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), left); - left = _block->TEMP(t); - } - - V4IR::Expr* right = *expression(ast->right); - - if (_expr.accept(cx)) { - cjump(binop(V4IR::binaryOperator(ast->op), left, right), _expr.iftrue, _expr.iffalse); - } else { - V4IR::Expr *e = binop(V4IR::binaryOperator(ast->op), left, right); - if (e->asConst() || e->asString()) - _expr.code = e; - else { - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), e); - _expr.code = _block->TEMP(t); - } - } - break; - } - - case QSOperator::Add: - case QSOperator::BitAnd: - case QSOperator::BitOr: - case QSOperator::BitXor: - case QSOperator::Div: - case QSOperator::LShift: - case QSOperator::Mod: - case QSOperator::Mul: - case QSOperator::RShift: - case QSOperator::Sub: - case QSOperator::URShift: { - if (!left->asTemp() && !left->asConst()) { - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), left); - left = _block->TEMP(t); - } - - V4IR::Expr* right = *expression(ast->right); - - V4IR::Expr *e = binop(V4IR::binaryOperator(ast->op), left, right); - if (e->asConst() || e->asString()) - _expr.code = e; - else { - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), e); - _expr.code = _block->TEMP(t); - } - break; - } - - } // switch - - return false; -} - -bool Codegen::visit(CallExpression *ast) -{ - Result base = expression(ast->base); - V4IR::ExprList *args = 0, **args_it = &args; - for (ArgumentList *it = ast->arguments; it; it = it->next) { - Result arg = expression(it->expression); - V4IR::Expr *actual = argument(*arg); - *args_it = _function->New(); - (*args_it)->init(actual); - args_it = &(*args_it)->next; - } - _expr.code = call(*base, args); - return false; -} - -bool Codegen::visit(ConditionalExpression *ast) -{ - V4IR::BasicBlock *iftrue = _function->newBasicBlock(groupStartBlock()); - V4IR::BasicBlock *iffalse = _function->newBasicBlock(groupStartBlock()); - V4IR::BasicBlock *endif = _function->newBasicBlock(groupStartBlock()); - - const unsigned t = _block->newTemp(); - - condition(ast->expression, iftrue, iffalse); - - _block = iftrue; - move(_block->TEMP(t), *expression(ast->ok)); - _block->JUMP(endif); - - _block = iffalse; - move(_block->TEMP(t), *expression(ast->ko)); - _block->JUMP(endif); - - _block = endif; - - _expr.code = _block->TEMP(t); - - return false; -} - -bool Codegen::visit(DeleteExpression *ast) -{ - V4IR::Expr* expr = *expression(ast->expression); - // Temporaries cannot be deleted - if (expr->asTemp() && expr->asTemp()->index < _env->members.size()) { - // Trying to delete a function argument might throw. - if (_function->isStrict && expr->asTemp()->index < 0) - throwSyntaxError(ast->deleteToken, "Delete of an unqualified identifier in strict mode."); - _expr.code = _block->CONST(V4IR::BoolType, 0); - return false; - } - if (_function->isStrict && expr->asName()) - throwSyntaxError(ast->deleteToken, "Delete of an unqualified identifier in strict mode."); - - // [[11.4.1]] Return true if it's not a reference - if (expr->asConst() || expr->asString()) { - _expr.code = _block->CONST(V4IR::BoolType, 1); - return false; - } - - // Return values from calls are also not a reference, but we have to - // perform the call to allow for side effects. - if (expr->asCall()) { - _block->EXP(expr); - _expr.code = _block->CONST(V4IR::BoolType, 1); - return false; - } - if (expr->asTemp() && expr->asTemp()->index >= _env->members.size()) { - _expr.code = _block->CONST(V4IR::BoolType, 1); - return false; - } - - V4IR::ExprList *args = _function->New(); - args->init(reference(expr)); - _expr.code = call(_block->NAME(V4IR::Name::builtin_delete, ast->deleteToken.startLine, ast->deleteToken.startColumn), args); - return false; -} - -bool Codegen::visit(FalseLiteral *) -{ - if (_expr.accept(cx)) { - _block->JUMP(_expr.iffalse); - } else { - _expr.code = _block->CONST(V4IR::BoolType, 0); - } - return false; -} - -bool Codegen::visit(FieldMemberExpression *ast) -{ - Result base = expression(ast->base); - _expr.code = member(*base, _function->newString(ast->name.toString())); - return false; -} - -bool Codegen::visit(FunctionExpression *ast) -{ - V4IR::Function *function = defineFunction(ast->name.toString(), ast, ast->formals, ast->body ? ast->body->elements : 0); - _expr.code = _block->CLOSURE(function); - return false; -} - -V4IR::Expr *Codegen::identifier(const QString &name, int line, int col) -{ - uint scope = 0; - Environment *e = _env; - V4IR::Function *f = _function; - - while (f && e->parent) { - if ((f->usesArgumentsObject && name == "arguments") || (!f->isStrict && f->hasDirectEval) || f->insideWithOrCatch || (f->isNamedExpression && f->name == name)) - break; - int index = e->findMember(name); - assert (index < e->members.size()); - if (index != -1) { - return _block->LOCAL(index, scope); - } - const int argIdx = f->indexOfArgument(&name); - if (argIdx != -1) - return _block->ARG(argIdx, scope); - ++scope; - e = e->parent; - f = f->outer; - } - - if (!e->parent && (!f || !f->insideWithOrCatch) && _mode != EvalCode && _mode != QmlBinding && (!f || f->name != name)) - return _block->GLOBALNAME(name, line, col); - - // global context or with. Lookup by name - return _block->NAME(name, line, col); - -} - -bool Codegen::visit(IdentifierExpression *ast) -{ - _expr.code = identifier(ast->name.toString(), ast->identifierToken.startLine, ast->identifierToken.startColumn); - return false; -} - -bool Codegen::visit(NestedExpression *ast) -{ - accept(ast->expression); - return false; -} - -bool Codegen::visit(NewExpression *ast) -{ - Result base = expression(ast->expression); - V4IR::Expr *expr = *base; - if (expr && !expr->asTemp() && !expr->asName() && !expr->asMember()) { - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), expr); - expr = _block->TEMP(t); - } - _expr.code = _block->NEW(expr, 0); - return false; -} - -bool Codegen::visit(NewMemberExpression *ast) -{ - Result base = expression(ast->base); - V4IR::Expr *expr = *base; - if (expr && !expr->asTemp() && !expr->asName() && !expr->asMember()) { - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), expr); - expr = _block->TEMP(t); - } - - V4IR::ExprList *args = 0, **args_it = &args; - for (ArgumentList *it = ast->arguments; it; it = it->next) { - Result arg = expression(it->expression); - V4IR::Expr *actual = argument(*arg); - *args_it = _function->New(); - (*args_it)->init(actual); - args_it = &(*args_it)->next; - } - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), _block->NEW(expr, args)); - _expr.code = _block->TEMP(t); - return false; -} - -bool Codegen::visit(NotExpression *ast) -{ - Result expr = expression(ast->expression); - const unsigned r = _block->newTemp(); - move(_block->TEMP(r), unop(V4IR::OpNot, *expr)); - _expr.code = _block->TEMP(r); - return false; -} - -bool Codegen::visit(NullExpression *) -{ - if (_expr.accept(cx)) _block->JUMP(_expr.iffalse); - else _expr.code = _block->CONST(V4IR::NullType, 0); - - return false; -} - -bool Codegen::visit(NumericLiteral *ast) -{ - if (_expr.accept(cx)) { - if (ast->value) _block->JUMP(_expr.iftrue); - else _block->JUMP(_expr.iffalse); - } else { - _expr.code = _block->CONST(V4IR::NumberType, ast->value); - } - return false; -} - -struct ObjectPropertyValue { - V4IR::Expr *value; - V4IR::Function *getter; - V4IR::Function *setter; -}; - -bool Codegen::visit(ObjectLiteral *ast) -{ - QMap valueMap; - - for (PropertyAssignmentList *it = ast->properties; it; it = it->next) { - if (PropertyNameAndValue *nv = AST::cast(it->assignment)) { - QString name = propertyName(nv->name); - Result value = expression(nv->value); - ObjectPropertyValue &v = valueMap[name]; - if (v.getter || v.setter || (_function->isStrict && v.value)) - throwSyntaxError(nv->lastSourceLocation(), - QCoreApplication::translate("qv4codegen", "Illegal duplicate key '%1' in object literal").arg(name)); - - valueMap[name].value = *value; - } else if (PropertyGetterSetter *gs = AST::cast(it->assignment)) { - QString name = propertyName(gs->name); - V4IR::Function *function = defineFunction(name, gs, gs->formals, gs->functionBody ? gs->functionBody->elements : 0); - ObjectPropertyValue &v = valueMap[name]; - if (v.value || - (gs->type == PropertyGetterSetter::Getter && v.getter) || - (gs->type == PropertyGetterSetter::Setter && v.setter)) - throwSyntaxError(gs->lastSourceLocation(), - QCoreApplication::translate("qv4codegen", "Illegal duplicate key '%1' in object literal").arg(name)); - if (gs->type == PropertyGetterSetter::Getter) - v.getter = function; - else - v.setter = function; - } else { - Q_UNREACHABLE(); - } - } - - V4IR::ExprList *args = 0; - - if (!valueMap.isEmpty()) { - V4IR::ExprList *current; - for (QMap::iterator it = valueMap.begin(); it != valueMap.end(); ) { - if (QV4::String(0, it.key()).asArrayIndex() != UINT_MAX) { - ++it; - continue; - } - - if (!args) { - args = _function->New(); - current = args; - } else { - current->next = _function->New(); - current = current->next; - } - - current->expr = _block->NAME(it.key(), 0, 0); - - if (it->value) { - current->next = _function->New(); - current = current->next; - current->expr = _block->CONST(V4IR::BoolType, true); - - unsigned value = _block->newTemp(); - move(_block->TEMP(value), it->value); - - current->next = _function->New(); - current = current->next; - current->expr = _block->TEMP(value); - } else { - current->next = _function->New(); - current = current->next; - current->expr = _block->CONST(V4IR::BoolType, false); - - unsigned getter = _block->newTemp(); - unsigned setter = _block->newTemp(); - move(_block->TEMP(getter), it->getter ? _block->CLOSURE(it->getter) : _block->CONST(V4IR::UndefinedType, 0)); - move(_block->TEMP(setter), it->setter ? _block->CLOSURE(it->setter) : _block->CONST(V4IR::UndefinedType, 0)); - - current->next = _function->New(); - current = current->next; - current->expr = _block->TEMP(getter); - current->next = _function->New(); - current = current->next; - current->expr = _block->TEMP(setter); - } - - it = valueMap.erase(it); - } - } - - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), _block->CALL(_block->NAME(V4IR::Name::builtin_define_object_literal, - ast->firstSourceLocation().startLine, ast->firstSourceLocation().startColumn), args)); - - // What's left are array entries - if (!valueMap.isEmpty()) { - unsigned value = 0; - unsigned getter = 0; - unsigned setter = 0; - for (QMap::const_iterator it = valueMap.constBegin(); it != valueMap.constEnd(); ++it) { - V4IR::ExprList *args = _function->New(); - V4IR::ExprList *current = args; - current->expr = _block->TEMP(t); - current->next = _function->New(); - current = current->next; - current->expr = _block->NAME(it.key(), 0, 0); - current->next = _function->New(); - current = current->next; - - if (it->value) { - if (!value) - value = _block->newTemp(); - move(_block->TEMP(value), it->value); - // __qmljs_builtin_define_property(Value object, String *name, Value val, ExecutionContext *ctx) - current->expr = _block->TEMP(value); - _block->EXP(_block->CALL(_block->NAME(V4IR::Name::builtin_define_property, 0, 0), args)); - } else { - if (!getter) { - getter = _block->newTemp(); - setter = _block->newTemp(); - } - move(_block->TEMP(getter), it->getter ? _block->CLOSURE(it->getter) : _block->CONST(V4IR::UndefinedType, 0)); - move(_block->TEMP(setter), it->setter ? _block->CLOSURE(it->setter) : _block->CONST(V4IR::UndefinedType, 0)); - - - // __qmljs_builtin_define_getter_setter(Value object, String *name, Value getter, Value setter, ExecutionContext *ctx); - current->expr = _block->TEMP(getter); - current->next = _function->New(); - current = current->next; - current->expr = _block->TEMP(setter); - _block->EXP(_block->CALL(_block->NAME(V4IR::Name::builtin_define_getter_setter, 0, 0), args)); - } - } - } - - _expr.code = _block->TEMP(t); - return false; -} - -bool Codegen::visit(PostDecrementExpression *ast) -{ - Result expr = expression(ast->base); - if (!expr->isLValue()) - throwReferenceError(ast->base->lastSourceLocation(), "Invalid left-hand side expression in postfix operation"); - throwSyntaxErrorOnEvalOrArgumentsInStrictMode(*expr, ast->decrementToken); - - if (_expr.accept(nx)) { - move(*expr, binop(V4IR::OpSub, *expr, _block->CONST(V4IR::NumberType, 1))); - } else { - V4IR::ExprList *args = _function->New(); - args->init(*expr); - _expr.code = call(_block->NAME(V4IR::Name::builtin_postdecrement, ast->lastSourceLocation().startLine, ast->lastSourceLocation().startColumn), args); - } - return false; -} - -bool Codegen::visit(PostIncrementExpression *ast) -{ - Result expr = expression(ast->base); - if (!expr->isLValue()) - throwReferenceError(ast->base->lastSourceLocation(), "Invalid left-hand side expression in postfix operation"); - throwSyntaxErrorOnEvalOrArgumentsInStrictMode(*expr, ast->incrementToken); - - if (_expr.accept(nx)) { - move(*expr, binop(V4IR::OpAdd, unop(V4IR::OpUPlus, *expr), _block->CONST(V4IR::NumberType, 1))); - } else { - V4IR::ExprList *args = _function->New(); - args->init(*expr); - _expr.code = call(_block->NAME(V4IR::Name::builtin_postincrement, ast->lastSourceLocation().startLine, ast->lastSourceLocation().startColumn), args); - } - return false; -} - -bool Codegen::visit(PreDecrementExpression *ast) -{ - Result expr = expression(ast->expression); - throwSyntaxErrorOnEvalOrArgumentsInStrictMode(*expr, ast->decrementToken); - V4IR::Expr *op = binop(V4IR::OpSub, *expr, _block->CONST(V4IR::NumberType, 1)); - if (_expr.accept(nx)) { - move(*expr, op); - } else { - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), op); - move(*expr, _block->TEMP(t)); - _expr.code = _block->TEMP(t); - } - return false; -} - -bool Codegen::visit(PreIncrementExpression *ast) -{ - Result expr = expression(ast->expression); - throwSyntaxErrorOnEvalOrArgumentsInStrictMode(*expr, ast->incrementToken); - V4IR::Expr *op = binop(V4IR::OpAdd, unop(V4IR::OpUPlus, *expr), _block->CONST(V4IR::NumberType, 1)); - if (_expr.accept(nx)) { - move(*expr, op); - } else { - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), op); - move(*expr, _block->TEMP(t)); - _expr.code = _block->TEMP(t); - } - return false; -} - -bool Codegen::visit(RegExpLiteral *ast) -{ - _expr.code = _block->REGEXP(_function->newString(ast->pattern.toString()), ast->flags); - return false; -} - -bool Codegen::visit(StringLiteral *ast) -{ - _expr.code = _block->STRING(_function->newString(ast->value.toString())); - return false; -} - -bool Codegen::visit(ThisExpression *ast) -{ - _expr.code = _block->NAME(QStringLiteral("this"), ast->thisToken.startLine, ast->thisToken.startColumn); - return false; -} - -bool Codegen::visit(TildeExpression *ast) -{ - Result expr = expression(ast->expression); - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), unop(V4IR::OpCompl, *expr)); - _expr.code = _block->TEMP(t); - return false; -} - -bool Codegen::visit(TrueLiteral *) -{ - if (_expr.accept(cx)) { - _block->JUMP(_expr.iftrue); - } else { - _expr.code = _block->CONST(V4IR::BoolType, 1); - } - return false; -} - -bool Codegen::visit(TypeOfExpression *ast) -{ - Result expr = expression(ast->expression); - V4IR::ExprList *args = _function->New(); - args->init(reference(*expr)); - _expr.code = call(_block->NAME(V4IR::Name::builtin_typeof, ast->typeofToken.startLine, ast->typeofToken.startColumn), args); - return false; -} - -bool Codegen::visit(UnaryMinusExpression *ast) -{ - Result expr = expression(ast->expression); - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), unop(V4IR::OpUMinus, *expr)); - _expr.code = _block->TEMP(t); - return false; -} - -bool Codegen::visit(UnaryPlusExpression *ast) -{ - Result expr = expression(ast->expression); - const unsigned t = _block->newTemp(); - move(_block->TEMP(t), unop(V4IR::OpUPlus, *expr)); - _expr.code = _block->TEMP(t); - return false; -} - -bool Codegen::visit(VoidExpression *ast) -{ - statement(ast->expression); - _expr.code = _block->CONST(V4IR::UndefinedType, 0); - return false; -} - -bool Codegen::visit(FunctionDeclaration * ast) -{ - if (_mode == QmlBinding) - move(_block->TEMP(_returnAddress), _block->NAME(ast->name.toString(), 0, 0)); - _expr.accept(nx); - return false; -} - -V4IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast, - AST::FormalParameterList *formals, - AST::SourceElements *body, Mode mode, - const QStringList &inheritedLocals) -{ - qSwap(_mode, mode); // enter function code. - Loop *loop = 0; - qSwap(_loop, loop); - - ScopeAndFinally *scopeAndFinally = 0; - - enterEnvironment(ast); - V4IR::Function *function = _module->newFunction(name, _function); - function->sourceFile = _fileName; - - V4IR::BasicBlock *entryBlock = function->newBasicBlock(groupStartBlock()); - V4IR::BasicBlock *exitBlock = function->newBasicBlock(groupStartBlock(), V4IR::Function::DontInsertBlock); - V4IR::BasicBlock *throwBlock = function->newBasicBlock(groupStartBlock()); - function->hasDirectEval = _env->hasDirectEval; - function->usesArgumentsObject = (_env->usesArgumentsObject == Environment::ArgumentsObjectUsed); - function->maxNumberOfArguments = _env->maxNumberOfArguments; - function->isStrict = _env->isStrict; - function->isNamedExpression = _env->isNamedFunctionExpression; - - // variables in global code are properties of the global context object, not locals as with other functions. - if (_mode == FunctionCode) { - unsigned t = 0; - for (Environment::MemberMap::iterator it = _env->members.begin(); it != _env->members.end(); ++it) { - const QString &local = it.key(); - function->LOCAL(local); - (*it).index = t; - entryBlock->MOVE(entryBlock->LOCAL(t, 0), entryBlock->CONST(V4IR::UndefinedType, 0)); - ++t; - } - } else { - if (!_env->isStrict) { - foreach (const QString &inheritedLocal, inheritedLocals) { - function->LOCAL(inheritedLocal); - unsigned tempIndex = entryBlock->newTemp(); - Environment::Member member = { Environment::UndefinedMember, - static_cast(tempIndex), 0 }; - _env->members.insert(inheritedLocal, member); - } - } - - V4IR::ExprList *args = 0; - for (Environment::MemberMap::const_iterator it = _env->members.constBegin(); it != _env->members.constEnd(); ++it) { - const QString &local = it.key(); - V4IR::ExprList *next = function->New(); - next->expr = entryBlock->NAME(local, 0, 0); - next->next = args; - args = next; - } - if (args) { - V4IR::ExprList *next = function->New(); - next->expr = entryBlock->CONST(V4IR::BoolType, (mode == EvalCode || mode == QmlBinding)); - next->next = args; - args = next; - - entryBlock->EXP(entryBlock->CALL(entryBlock->NAME(V4IR::Name::builtin_declare_vars, 0, 0), args)); - } - } - - unsigned returnAddress = entryBlock->newTemp(); - - entryBlock->MOVE(entryBlock->TEMP(returnAddress), entryBlock->CONST(V4IR::UndefinedType, 0)); - exitBlock->RET(exitBlock->TEMP(returnAddress)); - V4IR::ExprList *throwArgs = function->New(); - throwArgs->expr = throwBlock->TEMP(returnAddress); - throwBlock->EXP(throwBlock->CALL(throwBlock->NAME(V4IR::Name::builtin_throw, /*line*/0, /*column*/0), throwArgs)); - throwBlock->JUMP(exitBlock); - - qSwap(_function, function); - qSwap(_block, entryBlock); - qSwap(_exitBlock, exitBlock); - qSwap(_throwBlock, throwBlock); - qSwap(_returnAddress, returnAddress); - qSwap(_scopeAndFinally, scopeAndFinally); - - for (FormalParameterList *it = formals; it; it = it->next) { - _function->RECEIVE(it->name.toString()); - } - - foreach (const Environment::Member &member, _env->members) { - if (member.function) { - V4IR::Function *function = defineFunction(member.function->name.toString(), member.function, member.function->formals, - member.function->body ? member.function->body->elements : 0); - if (! _env->parent) { - move(_block->NAME(member.function->name.toString(), member.function->identifierToken.startLine, member.function->identifierToken.startColumn), - _block->CLOSURE(function)); - } else { - assert(member.index >= 0); - move(_block->LOCAL(member.index, 0), _block->CLOSURE(function)); - } - } - } - - sourceElements(body); - - _function->insertBasicBlock(_exitBlock); - - _block->JUMP(_exitBlock); - - qSwap(_function, function); - qSwap(_block, entryBlock); - qSwap(_exitBlock, exitBlock); - qSwap(_throwBlock, throwBlock); - qSwap(_returnAddress, returnAddress); - qSwap(_scopeAndFinally, scopeAndFinally); - qSwap(_loop, loop); - - leaveEnvironment(); - - qSwap(_mode, mode); - - return function; -} - -bool Codegen::visit(IdentifierPropertyName *ast) -{ - _property = ast->id.toString(); - return false; -} - -bool Codegen::visit(NumericLiteralPropertyName *ast) -{ - _property = QString::number(ast->id, 'g', 16); - return false; -} - -bool Codegen::visit(StringLiteralPropertyName *ast) -{ - _property = ast->id.toString(); - return false; -} - -bool Codegen::visit(FunctionSourceElement *ast) -{ - statement(ast->declaration); - return false; -} - -bool Codegen::visit(StatementSourceElement *ast) -{ - statement(ast->statement); - return false; -} - -bool Codegen::visit(Block *ast) -{ - for (StatementList *it = ast->statements; it; it = it->next) { - statement(it->statement); - } - return false; -} - -bool Codegen::visit(BreakStatement *ast) -{ - if (!_loop) - throwSyntaxError(ast->lastSourceLocation(), QCoreApplication::translate("qv4codegen", "Break outside of loop")); - Loop *loop = 0; - if (ast->label.isEmpty()) - loop = _loop; - else { - for (loop = _loop; loop; loop = loop->parent) { - if (loop->labelledStatement && loop->labelledStatement->label == ast->label) - break; - } - if (!loop) - throwSyntaxError(ast->lastSourceLocation(), QCoreApplication::translate("qv4codegen", "Undefined label '%1'").arg(ast->label.toString())); - } - unwindException(loop->scopeAndFinally); - _block->JUMP(loop->breakBlock); - return false; -} - -bool Codegen::visit(ContinueStatement *ast) -{ - Loop *loop = 0; - if (ast->label.isEmpty()) { - for (loop = _loop; loop; loop = loop->parent) { - if (loop->continueBlock) - break; - } - } else { - for (loop = _loop; loop; loop = loop->parent) { - if (loop->labelledStatement && loop->labelledStatement->label == ast->label) { - if (!loop->continueBlock) - loop = 0; - break; - } - } - if (!loop) - throwSyntaxError(ast->lastSourceLocation(), QCoreApplication::translate("qv4codegen", "Undefined label '%1'").arg(ast->label.toString())); - } - if (!loop) - throwSyntaxError(ast->lastSourceLocation(), QCoreApplication::translate("qv4codegen", "continue outside of loop")); - unwindException(loop->scopeAndFinally); - _block->JUMP(loop->continueBlock); - return false; -} - -bool Codegen::visit(DebuggerStatement *) -{ - Q_UNIMPLEMENTED(); - return false; -} - -bool Codegen::visit(DoWhileStatement *ast) -{ - V4IR::BasicBlock *loopbody = _function->newBasicBlock(groupStartBlock()); - V4IR::BasicBlock *loopcond = _function->newBasicBlock(loopbody); - V4IR::BasicBlock *loopend = _function->newBasicBlock(groupStartBlock()); - - enterLoop(ast, loopbody, loopend, loopcond); - - _block->JUMP(loopbody); - - _block = loopbody; - statement(ast->statement); - _block->JUMP(loopcond); - - _block = loopcond; - condition(ast->expression, loopbody, loopend); - - _block = loopend; - - leaveLoop(); - - return false; -} - -bool Codegen::visit(EmptyStatement *) -{ - return false; -} - -bool Codegen::visit(ExpressionStatement *ast) -{ - if (_mode == EvalCode || _mode == QmlBinding) { - Result e = expression(ast->expression); - if (*e) - move(_block->TEMP(_returnAddress), *e); - } else { - statement(ast->expression); - } - return false; -} - -bool Codegen::visit(ForEachStatement *ast) -{ - V4IR::BasicBlock *foreachin = _function->newBasicBlock(groupStartBlock()); - V4IR::BasicBlock *foreachbody = _function->newBasicBlock(foreachin); - V4IR::BasicBlock *foreachend = _function->newBasicBlock(groupStartBlock()); - - enterLoop(ast, foreachin, foreachend, foreachin); - - int objectToIterateOn = _block->newTemp(); - move(_block->TEMP(objectToIterateOn), *expression(ast->expression)); - V4IR::ExprList *args = _function->New(); - args->init(_block->TEMP(objectToIterateOn)); - - int iterator = _block->newTemp(); - move(_block->TEMP(iterator), _block->CALL(_block->NAME(V4IR::Name::builtin_foreach_iterator_object, 0, 0), args)); - - _block->JUMP(foreachin); - - _block = foreachbody; - int temp = _block->newTemp(); - move(*expression(ast->initialiser), _block->TEMP(temp)); - statement(ast->statement); - _block->JUMP(foreachin); - - _block = foreachin; - - args = _function->New(); - args->init(_block->TEMP(iterator)); - move(_block->TEMP(temp), _block->CALL(_block->NAME(V4IR::Name::builtin_foreach_next_property_name, 0, 0), args)); - int null = _block->newTemp(); - move(_block->TEMP(null), _block->CONST(V4IR::NullType, 0)); - cjump(_block->BINOP(V4IR::OpStrictNotEqual, _block->TEMP(temp), _block->TEMP(null)), foreachbody, foreachend); - _block = foreachend; - - leaveLoop(); - return false; -} - -bool Codegen::visit(ForStatement *ast) -{ - V4IR::BasicBlock *forcond = _function->newBasicBlock(groupStartBlock()); - V4IR::BasicBlock *forbody = _function->newBasicBlock(forcond); - V4IR::BasicBlock *forstep = _function->newBasicBlock(forcond); - V4IR::BasicBlock *forend = _function->newBasicBlock(groupStartBlock()); - - enterLoop(ast, forcond, forend, forstep); - - statement(ast->initialiser); - _block->JUMP(forcond); - - _block = forcond; - if (ast->condition) - condition(ast->condition, forbody, forend); - else - _block->JUMP(forbody); - - _block = forbody; - statement(ast->statement); - _block->JUMP(forstep); - - _block = forstep; - statement(ast->expression); - _block->JUMP(forcond); - - _block = forend; - - leaveLoop(); - - return false; -} - -bool Codegen::visit(IfStatement *ast) -{ - V4IR::BasicBlock *iftrue = _function->newBasicBlock(groupStartBlock()); - V4IR::BasicBlock *iffalse = ast->ko ? _function->newBasicBlock(groupStartBlock()) : 0; - V4IR::BasicBlock *endif = _function->newBasicBlock(groupStartBlock()); - condition(ast->expression, iftrue, ast->ko ? iffalse : endif); - - _block = iftrue; - statement(ast->ok); - _block->JUMP(endif); - - if (ast->ko) { - _block = iffalse; - statement(ast->ko); - _block->JUMP(endif); - } - - _block = endif; - - return false; -} - -bool Codegen::visit(LabelledStatement *ast) -{ - // check that no outer loop contains the label - Loop *l = _loop; - while (l) { - if (l->labelledStatement->label == ast->label) { - QString error = QString(QStringLiteral("Label '%1' has already been declared")).arg(ast->label.toString()); - throwSyntaxError(ast->firstSourceLocation(), error); - } - l = l->parent; - } - _labelledStatement = ast; - - if (AST::cast(ast->statement) || - AST::cast(ast->statement) || - AST::cast(ast->statement) || - AST::cast(ast->statement) || - AST::cast(ast->statement) || - AST::cast(ast->statement) || - AST::cast(ast->statement)) { - statement(ast->statement); // labelledStatement will be associated with the ast->statement's loop. - } else { - V4IR::BasicBlock *breakBlock = _function->newBasicBlock(groupStartBlock()); - enterLoop(ast->statement, 0, breakBlock, /*continueBlock*/ 0); - statement(ast->statement); - _block->JUMP(breakBlock); - _block = breakBlock; - leaveLoop(); - } - - return false; -} - -bool Codegen::visit(LocalForEachStatement *ast) -{ - V4IR::BasicBlock *foreachin = _function->newBasicBlock(groupStartBlock()); - V4IR::BasicBlock *foreachbody = _function->newBasicBlock(foreachin); - V4IR::BasicBlock *foreachend = _function->newBasicBlock(groupStartBlock()); - - enterLoop(ast, foreachin, foreachend, foreachin); - - variableDeclaration(ast->declaration); - - int iterator = _block->newTemp(); - move(_block->TEMP(iterator), *expression(ast->expression)); - V4IR::ExprList *args = _function->New(); - args->init(_block->TEMP(iterator)); - move(_block->TEMP(iterator), _block->CALL(_block->NAME(V4IR::Name::builtin_foreach_iterator_object, 0, 0), args)); - - _block->JUMP(foreachin); - - _block = foreachbody; - int temp = _block->newTemp(); - move(identifier(ast->declaration->name.toString()), _block->TEMP(temp)); - statement(ast->statement); - _block->JUMP(foreachin); - - _block = foreachin; - - args = _function->New(); - args->init(_block->TEMP(iterator)); - move(_block->TEMP(temp), _block->CALL(_block->NAME(V4IR::Name::builtin_foreach_next_property_name, 0, 0), args)); - int null = _block->newTemp(); - move(_block->TEMP(null), _block->CONST(V4IR::NullType, 0)); - cjump(_block->BINOP(V4IR::OpStrictNotEqual, _block->TEMP(temp), _block->TEMP(null)), foreachbody, foreachend); - _block = foreachend; - - leaveLoop(); - return false; -} - -bool Codegen::visit(LocalForStatement *ast) -{ - V4IR::BasicBlock *forcond = _function->newBasicBlock(groupStartBlock()); - V4IR::BasicBlock *forbody = _function->newBasicBlock(forcond); - V4IR::BasicBlock *forstep = _function->newBasicBlock(forcond); - V4IR::BasicBlock *forend = _function->newBasicBlock(groupStartBlock()); - - enterLoop(ast, forcond, forend, forstep); - - variableDeclarationList(ast->declarations); - _block->JUMP(forcond); - - _block = forcond; - if (ast->condition) - condition(ast->condition, forbody, forend); - else - _block->JUMP(forbody); - - _block = forbody; - statement(ast->statement); - _block->JUMP(forstep); - - _block = forstep; - statement(ast->expression); - _block->JUMP(forcond); - - _block = forend; - - leaveLoop(); - - return false; -} - -bool Codegen::visit(ReturnStatement *ast) -{ - if (_mode != FunctionCode && _mode != QmlBinding) - throwSyntaxError(ast->returnToken, QCoreApplication::translate("qv4codegen", "Return statement outside of function")); - if (ast->expression) { - Result expr = expression(ast->expression); - move(_block->TEMP(_returnAddress), *expr); - } - unwindException(0); - - _block->JUMP(_exitBlock); - return false; -} - -bool Codegen::visit(SwitchStatement *ast) -{ - V4IR::BasicBlock *switchend = _function->newBasicBlock(groupStartBlock()); - - if (ast->block) { - Result lhs = expression(ast->expression); - V4IR::BasicBlock *switchcond = _function->newBasicBlock(groupStartBlock()); - _block->JUMP(switchcond); - V4IR::BasicBlock *previousBlock = 0; - - QHash blockMap; - - enterLoop(ast, 0, switchend, 0); - - for (CaseClauses *it = ast->block->clauses; it; it = it->next) { - CaseClause *clause = it->clause; - - _block = _function->newBasicBlock(groupStartBlock()); - blockMap[clause] = _block; - - if (previousBlock && !previousBlock->isTerminated()) - previousBlock->JUMP(_block); - - for (StatementList *it2 = clause->statements; it2; it2 = it2->next) - statement(it2->statement); - - previousBlock = _block; - } - - if (ast->block->defaultClause) { - _block = _function->newBasicBlock(groupStartBlock()); - blockMap[ast->block->defaultClause] = _block; - - if (previousBlock && !previousBlock->isTerminated()) - previousBlock->JUMP(_block); - - for (StatementList *it2 = ast->block->defaultClause->statements; it2; it2 = it2->next) - statement(it2->statement); - - previousBlock = _block; - } - - for (CaseClauses *it = ast->block->moreClauses; it; it = it->next) { - CaseClause *clause = it->clause; - - _block = _function->newBasicBlock(groupStartBlock()); - blockMap[clause] = _block; - - if (previousBlock && !previousBlock->isTerminated()) - previousBlock->JUMP(_block); - - for (StatementList *it2 = clause->statements; it2; it2 = it2->next) - statement(it2->statement); - - previousBlock = _block; - } - - leaveLoop(); - - _block->JUMP(switchend); - - _block = switchcond; - for (CaseClauses *it = ast->block->clauses; it; it = it->next) { - CaseClause *clause = it->clause; - Result rhs = expression(clause->expression); - V4IR::BasicBlock *iftrue = blockMap[clause]; - V4IR::BasicBlock *iffalse = _function->newBasicBlock(groupStartBlock()); - cjump(binop(V4IR::OpStrictEqual, *lhs, *rhs), iftrue, iffalse); - _block = iffalse; - } - - for (CaseClauses *it = ast->block->moreClauses; it; it = it->next) { - CaseClause *clause = it->clause; - Result rhs = expression(clause->expression); - V4IR::BasicBlock *iftrue = blockMap[clause]; - V4IR::BasicBlock *iffalse = _function->newBasicBlock(groupStartBlock()); - cjump(binop(V4IR::OpStrictEqual, *lhs, *rhs), iftrue, iffalse); - _block = iffalse; - } - - if (ast->block->defaultClause) { - _block->JUMP(blockMap[ast->block->defaultClause]); - } - } - - _block->JUMP(switchend); - - _block = switchend; - return false; -} - -bool Codegen::visit(ThrowStatement *ast) -{ - Result expr = expression(ast->expression); - move(_block->TEMP(_returnAddress), *expr); - _block->JUMP(_throwBlock); - return false; -} - -bool Codegen::visit(TryStatement *ast) -{ - _function->hasTry = true; - - if (_function->isStrict && ast->catchExpression && - (ast->catchExpression->name == QLatin1String("eval") || ast->catchExpression->name == QLatin1String("arguments"))) - throwSyntaxError(ast->catchExpression->identifierToken, QCoreApplication::translate("qv4codegen", "Catch variable name may not be eval or arguments in strict mode")); - - V4IR::BasicBlock *tryBody = _function->newBasicBlock(groupStartBlock()); - V4IR::BasicBlock *catchBody = _function->newBasicBlock(groupStartBlock()); - // We always need a finally body to clean up the exception handler - V4IR::BasicBlock *finallyBody = _function->newBasicBlock(groupStartBlock()); - - V4IR::BasicBlock *throwBlock = _function->newBasicBlock(groupStartBlock()); - V4IR::ExprList *throwArgs = _function->New(); - throwArgs->expr = throwBlock->TEMP(_returnAddress); - throwBlock->EXP(throwBlock->CALL(throwBlock->NAME(V4IR::Name::builtin_throw, /*line*/0, /*column*/0), throwArgs)); - throwBlock->JUMP(catchBody); - qSwap(_throwBlock, throwBlock); - - int hasException = _block->newTemp(); - move(_block->TEMP(hasException), _block->CONST(V4IR::BoolType, false)); - - // Pass the hidden "needRethrow" TEMP to the - // builtin_delete_exception_handler, in order to have those TEMPs alive for - // the duration of the exception handling block. - V4IR::ExprList *finishTryArgs = _function->New(); - finishTryArgs->init(_block->TEMP(hasException)); - - ScopeAndFinally tcf(_scopeAndFinally, ast->finallyExpression, finishTryArgs); - _scopeAndFinally = &tcf; - - int exception_to_rethrow = _block->newTemp(); - - _block->TRY(tryBody, catchBody, - ast->catchExpression ? ast->catchExpression->name.toString() : QString(), - _block->TEMP(exception_to_rethrow)); - - _block = tryBody; - statement(ast->statement); - _block->JUMP(finallyBody); - - _block = catchBody; - - if (ast->catchExpression) { - // check if an exception got thrown within catch. Go to finally - // and then rethrow - V4IR::BasicBlock *b = _function->newBasicBlock(groupStartBlock()); - _block->CJUMP(_block->TEMP(hasException), finallyBody, b); - _block = b; - } - - move(_block->TEMP(hasException), _block->CONST(V4IR::BoolType, true)); - - if (ast->catchExpression) { - ++_function->insideWithOrCatch; - { - ScopeAndFinally scope(_scopeAndFinally, ScopeAndFinally::CatchScope); - _scopeAndFinally = &scope; - statement(ast->catchExpression->statement); - _scopeAndFinally = scope.parent; - } - --_function->insideWithOrCatch; - move(_block->TEMP(hasException), _block->CONST(V4IR::BoolType, false)); - } - _block->JUMP(finallyBody); - - _scopeAndFinally = tcf.parent; - - qSwap(_throwBlock, throwBlock); - - V4IR::BasicBlock *after = _function->newBasicBlock(groupStartBlock()); - _block = finallyBody; - - _block->EXP(_block->CALL(_block->NAME(V4IR::Name::builtin_finish_try, 0, 0), finishTryArgs)); - - if (ast->finallyExpression && ast->finallyExpression->statement) - statement(ast->finallyExpression->statement); - - V4IR::BasicBlock *rethrowBlock = _function->newBasicBlock(groupStartBlock()); - _block->CJUMP(_block->TEMP(hasException), rethrowBlock, after); - _block = rethrowBlock; - move(_block->TEMP(_returnAddress), _block->TEMP(exception_to_rethrow)); - _block->JUMP(_throwBlock); - - _block = after; - - return false; -} - -void Codegen::unwindException(Codegen::ScopeAndFinally *outest) -{ - int savedDepthForWidthOrCatch = _function->insideWithOrCatch; - ScopeAndFinally *scopeAndFinally = _scopeAndFinally; - qSwap(_scopeAndFinally, scopeAndFinally); - while (_scopeAndFinally != outest) { - switch (_scopeAndFinally->type) { - case ScopeAndFinally::WithScope: - _block->EXP(_block->CALL(_block->NAME(V4IR::Name::builtin_pop_scope, 0, 0))); - // fall through - case ScopeAndFinally::CatchScope: - _scopeAndFinally = _scopeAndFinally->parent; - --_function->insideWithOrCatch; - break; - case ScopeAndFinally::TryScope: { - _block->EXP(_block->CALL(_block->NAME(V4IR::Name::builtin_finish_try, 0, 0), _scopeAndFinally->finishTryArgs)); - ScopeAndFinally *tc = _scopeAndFinally; - _scopeAndFinally = tc->parent; - if (tc->finally && tc->finally->statement) - statement(tc->finally->statement); - break; - } - } - } - qSwap(_scopeAndFinally, scopeAndFinally); - _function->insideWithOrCatch = savedDepthForWidthOrCatch; -} - -bool Codegen::visit(VariableStatement *ast) -{ - variableDeclarationList(ast->declarations); - return false; -} - -bool Codegen::visit(WhileStatement *ast) -{ - V4IR::BasicBlock *whilecond = _function->newBasicBlock(groupStartBlock()); - V4IR::BasicBlock *whilebody = _function->newBasicBlock(whilecond); - V4IR::BasicBlock *whileend = _function->newBasicBlock(groupStartBlock()); - - enterLoop(ast, whilecond, whileend, whilecond); - - _block->JUMP(whilecond); - _block = whilecond; - condition(ast->expression, whilebody, whileend); - - _block = whilebody; - statement(ast->statement); - _block->JUMP(whilecond); - - _block = whileend; - leaveLoop(); - - return false; -} - -bool Codegen::visit(WithStatement *ast) -{ - _function->hasWith = true; - - V4IR::BasicBlock *withBlock = _function->newBasicBlock(groupStartBlock()); - - _block->JUMP(withBlock); - _block = withBlock; - int withObject = _block->newTemp(); - _block->MOVE(_block->TEMP(withObject), *expression(ast->expression)); - V4IR::ExprList *args = _function->New(); - args->init(_block->TEMP(withObject)); - _block->EXP(_block->CALL(_block->NAME(V4IR::Name::builtin_push_with_scope, 0, 0), args)); - - ++_function->insideWithOrCatch; - { - ScopeAndFinally scope(_scopeAndFinally); - _scopeAndFinally = &scope; - statement(ast->statement); - _scopeAndFinally = scope.parent; - } - --_function->insideWithOrCatch; - _block->EXP(_block->CALL(_block->NAME(V4IR::Name::builtin_pop_scope, 0, 0), 0)); - - V4IR::BasicBlock *next = _function->newBasicBlock(groupStartBlock()); - _block->JUMP(next); - _block = next; - - return false; -} - -bool Codegen::visit(UiArrayBinding *) -{ - assert(!"not implemented"); - return false; -} - -bool Codegen::visit(UiObjectBinding *) -{ - assert(!"not implemented"); - return false; -} - -bool Codegen::visit(UiObjectDefinition *) -{ - assert(!"not implemented"); - return false; -} - -bool Codegen::visit(UiPublicMember *) -{ - assert(!"not implemented"); - return false; -} - -bool Codegen::visit(UiScriptBinding *) -{ - assert(!"not implemented"); - return false; -} - -bool Codegen::visit(UiSourceElement *) -{ - assert(!"not implemented"); - return false; -} - -void Codegen::throwSyntaxErrorOnEvalOrArgumentsInStrictMode(V4IR::Expr *expr, const SourceLocation& loc) -{ - if (!_env->isStrict) - return; - V4IR::Name *n = expr->asName(); - if (!n) - return; - if (*n->id == QLatin1String("eval") || *n->id == QLatin1String("arguments")) - throwSyntaxError(loc, QCoreApplication::translate("qv4codegen", "Variable name may not be eval or arguments in strict mode")); -} - -void Codegen::throwSyntaxError(const SourceLocation &loc, const QString &detail) -{ - QV4::DiagnosticMessage *msg = new QV4::DiagnosticMessage; - msg->fileName = _fileName; - msg->offset = loc.begin(); - msg->startLine = loc.startLine; - msg->startColumn = loc.startColumn; - msg->message = detail; - if (_context) - _context->throwSyntaxError(msg); - else if (_errorHandler) - _errorHandler->syntaxError(msg); - else - Q_ASSERT(!"No error handler available."); -} - -void Codegen::throwReferenceError(const SourceLocation &loc, const QString &detail) -{ - if (_context) - _context->throwReferenceError(QV4::Value::fromString(_context, detail), _fileName, loc.startLine); - else if (_errorHandler) - throwSyntaxError(loc, detail); - else - Q_ASSERT(!"No error handler available."); -} diff --git a/src/qml/qml/v4/qv4codegen_p.h b/src/qml/qml/v4/qv4codegen_p.h deleted file mode 100644 index fe00d87852..0000000000 --- a/src/qml/qml/v4/qv4codegen_p.h +++ /dev/null @@ -1,451 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QV4CODEGEN_P_H -#define QV4CODEGEN_P_H - -#include "qv4global_p.h" -#include "qv4jsir_p.h" -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE - -namespace QV4 { -struct DiagnosticMessage; -struct ExecutionContext; -} - -namespace QQmlJS { -namespace AST { -class UiParameterList; -} - - - -class ErrorHandler -{ -public: - virtual void syntaxError(QV4::DiagnosticMessage *message) = 0; -}; - -class Q_QML_EXPORT Codegen: protected AST::Visitor -{ -public: - Codegen(QV4::ExecutionContext *ctx, bool strict); - Codegen(ErrorHandler *errorHandler, bool strictMode); - - enum Mode { - GlobalCode, - EvalCode, - FunctionCode, - QmlBinding - }; - - V4IR::Function *operator()(const QString &fileName, - const QString &sourceCode, - AST::Program *ast, - V4IR::Module *module, - Mode mode = GlobalCode, - const QStringList &inheritedLocals = QStringList()); - V4IR::Function *operator()(const QString &fileName, - const QString &sourceCode, - AST::FunctionExpression *ast, - V4IR::Module *module); - -protected: - enum Format { ex, cx, nx }; - struct Result { - V4IR::Expr *code; - V4IR::BasicBlock *iftrue; - V4IR::BasicBlock *iffalse; - Format format; - Format requested; - - explicit Result(Format requested = ex) - : code(0) - , iftrue(0) - , iffalse(0) - , format(ex) - , requested(requested) {} - - explicit Result(V4IR::BasicBlock *iftrue, V4IR::BasicBlock *iffalse) - : code(0) - , iftrue(iftrue) - , iffalse(iffalse) - , format(ex) - , requested(cx) {} - - inline V4IR::Expr *operator*() const { Q_ASSERT(format == ex); return code; } - inline V4IR::Expr *operator->() const { Q_ASSERT(format == ex); return code; } - - bool accept(Format f) - { - if (requested == f) { - format = f; - return true; - } - return false; - } - }; - - struct Environment { - Environment *parent; - - enum MemberType { - UndefinedMember, - VariableDefinition, - VariableDeclaration, - FunctionDefinition - }; - struct Member { - MemberType type; - int index; - AST::FunctionExpression *function; - }; - typedef QMap MemberMap; - - MemberMap members; - AST::FormalParameterList *formals; - int maxNumberOfArguments; - bool hasDirectEval; - bool hasNestedFunctions; - bool isStrict; - bool isNamedFunctionExpression; - enum UsesArgumentsObject { - ArgumentsObjectUnknown, - ArgumentsObjectNotUsed, - ArgumentsObjectUsed - }; - - UsesArgumentsObject usesArgumentsObject; - - Environment(Environment *parent) - : parent(parent) - , formals(0) - , maxNumberOfArguments(0) - , hasDirectEval(false) - , hasNestedFunctions(false) - , isStrict(false) - , isNamedFunctionExpression(false) - , usesArgumentsObject(ArgumentsObjectUnknown) - { - if (parent && parent->isStrict) - isStrict = true; - } - - int findMember(const QString &name) const - { - MemberMap::const_iterator it = members.find(name); - if (it == members.end()) - return -1; - assert((*it).index != -1 || !parent); - return (*it).index; - } - - bool lookupMember(const QString &name, Environment **scope, int *index, int *distance) - { - Environment *it = this; - *distance = 0; - for (; it; it = it->parent, ++(*distance)) { - int idx = it->findMember(name); - if (idx != -1) { - *scope = it; - *index = idx; - return true; - } - } - return false; - } - - void enter(const QString &name, MemberType type, AST::FunctionExpression *function = 0) - { - if (! name.isEmpty()) { - if (type != FunctionDefinition) { - for (AST::FormalParameterList *it = formals; it; it = it->next) - if (it->name == name) - return; - } - MemberMap::iterator it = members.find(name); - if (it == members.end()) { - Member m; - m.index = -1; - m.type = type; - m.function = function; - members.insert(name, m); - } else { - if ((*it).type <= type) { - (*it).type = type; - (*it).function = function; - } - } - } - } - }; - - Environment *newEnvironment(AST::Node *node, Environment *parent) - { - Environment *env = new Environment(parent); - _envMap.insert(node, env); - return env; - } - - struct UiMember { - }; - - struct ScopeAndFinally { - enum ScopeType { - WithScope, - TryScope, - CatchScope - }; - - ScopeAndFinally *parent; - AST::Finally *finally; - V4IR::ExprList *finishTryArgs; - ScopeType type; - - ScopeAndFinally(ScopeAndFinally *parent, ScopeType t = WithScope) : parent(parent), finally(0), finishTryArgs(0), type(t) {} - ScopeAndFinally(ScopeAndFinally *parent, AST::Finally *finally, V4IR::ExprList *finishTryArgs) - : parent(parent), finally(finally), finishTryArgs(finishTryArgs), type(TryScope) - {} - }; - - struct Loop { - AST::LabelledStatement *labelledStatement; - AST::Statement *node; - V4IR::BasicBlock *groupStartBlock; - V4IR::BasicBlock *breakBlock; - V4IR::BasicBlock *continueBlock; - Loop *parent; - ScopeAndFinally *scopeAndFinally; - - Loop(AST::Statement *node, V4IR::BasicBlock *groupStartBlock, V4IR::BasicBlock *breakBlock, V4IR::BasicBlock *continueBlock, Loop *parent) - : labelledStatement(0), node(node), groupStartBlock(groupStartBlock), breakBlock(breakBlock), continueBlock(continueBlock), parent(parent) {} - }; - - void enterEnvironment(AST::Node *node); - void leaveEnvironment(); - - void enterLoop(AST::Statement *node, V4IR::BasicBlock *startBlock, V4IR::BasicBlock *breakBlock, V4IR::BasicBlock *continueBlock); - void leaveLoop(); - V4IR::BasicBlock *groupStartBlock() const - { - for (Loop *it = _loop; it; it = it->parent) - if (it->groupStartBlock) - return it->groupStartBlock; - return 0; - } - - V4IR::Expr *member(V4IR::Expr *base, const QString *name); - V4IR::Expr *subscript(V4IR::Expr *base, V4IR::Expr *index); - V4IR::Expr *argument(V4IR::Expr *expr); - V4IR::Expr *reference(V4IR::Expr *expr); - V4IR::Expr *unop(V4IR::AluOp op, V4IR::Expr *expr); - V4IR::Expr *binop(V4IR::AluOp op, V4IR::Expr *left, V4IR::Expr *right); - V4IR::Expr *call(V4IR::Expr *base, V4IR::ExprList *args); - void move(V4IR::Expr *target, V4IR::Expr *source, V4IR::AluOp op = V4IR::OpInvalid); - void cjump(V4IR::Expr *cond, V4IR::BasicBlock *iftrue, V4IR::BasicBlock *iffalse); - - V4IR::Function *defineFunction(const QString &name, AST::Node *ast, - AST::FormalParameterList *formals, - AST::SourceElements *body, - Mode mode = FunctionCode, - const QStringList &inheritedLocals = QStringList()); - - void unwindException(ScopeAndFinally *outest); - - void statement(AST::Statement *ast); - void statement(AST::ExpressionNode *ast); - void condition(AST::ExpressionNode *ast, V4IR::BasicBlock *iftrue, V4IR::BasicBlock *iffalse); - Result expression(AST::ExpressionNode *ast); - QString propertyName(AST::PropertyName *ast); - Result sourceElement(AST::SourceElement *ast); - UiMember uiObjectMember(AST::UiObjectMember *ast); - - void accept(AST::Node *node); - - void functionBody(AST::FunctionBody *ast); - void program(AST::Program *ast); - void sourceElements(AST::SourceElements *ast); - void variableDeclaration(AST::VariableDeclaration *ast); - void variableDeclarationList(AST::VariableDeclarationList *ast); - - V4IR::Expr *identifier(const QString &name, int line = 0, int col = 0); - - // nodes - virtual bool visit(AST::ArgumentList *ast); - virtual bool visit(AST::CaseBlock *ast); - virtual bool visit(AST::CaseClause *ast); - virtual bool visit(AST::CaseClauses *ast); - virtual bool visit(AST::Catch *ast); - virtual bool visit(AST::DefaultClause *ast); - virtual bool visit(AST::ElementList *ast); - virtual bool visit(AST::Elision *ast); - virtual bool visit(AST::Finally *ast); - virtual bool visit(AST::FormalParameterList *ast); - virtual bool visit(AST::FunctionBody *ast); - virtual bool visit(AST::Program *ast); - virtual bool visit(AST::PropertyNameAndValue *ast); - virtual bool visit(AST::PropertyAssignmentList *ast); - virtual bool visit(AST::PropertyGetterSetter *ast); - virtual bool visit(AST::SourceElements *ast); - virtual bool visit(AST::StatementList *ast); - virtual bool visit(AST::UiArrayMemberList *ast); - virtual bool visit(AST::UiImport *ast); - virtual bool visit(AST::UiImportList *ast); - virtual bool visit(AST::UiObjectInitializer *ast); - virtual bool visit(AST::UiObjectMemberList *ast); - virtual bool visit(AST::UiParameterList *ast); - virtual bool visit(AST::UiProgram *ast); - virtual bool visit(AST::UiQualifiedId *ast); - virtual bool visit(AST::VariableDeclaration *ast); - virtual bool visit(AST::VariableDeclarationList *ast); - - // expressions - virtual bool visit(AST::Expression *ast); - virtual bool visit(AST::ArrayLiteral *ast); - virtual bool visit(AST::ArrayMemberExpression *ast); - virtual bool visit(AST::BinaryExpression *ast); - virtual bool visit(AST::CallExpression *ast); - virtual bool visit(AST::ConditionalExpression *ast); - virtual bool visit(AST::DeleteExpression *ast); - virtual bool visit(AST::FalseLiteral *ast); - virtual bool visit(AST::FieldMemberExpression *ast); - virtual bool visit(AST::FunctionExpression *ast); - virtual bool visit(AST::IdentifierExpression *ast); - virtual bool visit(AST::NestedExpression *ast); - virtual bool visit(AST::NewExpression *ast); - virtual bool visit(AST::NewMemberExpression *ast); - virtual bool visit(AST::NotExpression *ast); - virtual bool visit(AST::NullExpression *ast); - virtual bool visit(AST::NumericLiteral *ast); - virtual bool visit(AST::ObjectLiteral *ast); - virtual bool visit(AST::PostDecrementExpression *ast); - virtual bool visit(AST::PostIncrementExpression *ast); - virtual bool visit(AST::PreDecrementExpression *ast); - virtual bool visit(AST::PreIncrementExpression *ast); - virtual bool visit(AST::RegExpLiteral *ast); - virtual bool visit(AST::StringLiteral *ast); - virtual bool visit(AST::ThisExpression *ast); - virtual bool visit(AST::TildeExpression *ast); - virtual bool visit(AST::TrueLiteral *ast); - virtual bool visit(AST::TypeOfExpression *ast); - virtual bool visit(AST::UnaryMinusExpression *ast); - virtual bool visit(AST::UnaryPlusExpression *ast); - virtual bool visit(AST::VoidExpression *ast); - virtual bool visit(AST::FunctionDeclaration *ast); - - // property names - virtual bool visit(AST::IdentifierPropertyName *ast); - virtual bool visit(AST::NumericLiteralPropertyName *ast); - virtual bool visit(AST::StringLiteralPropertyName *ast); - - // source elements - virtual bool visit(AST::FunctionSourceElement *ast); - virtual bool visit(AST::StatementSourceElement *ast); - - // statements - virtual bool visit(AST::Block *ast); - virtual bool visit(AST::BreakStatement *ast); - virtual bool visit(AST::ContinueStatement *ast); - virtual bool visit(AST::DebuggerStatement *ast); - virtual bool visit(AST::DoWhileStatement *ast); - virtual bool visit(AST::EmptyStatement *ast); - virtual bool visit(AST::ExpressionStatement *ast); - virtual bool visit(AST::ForEachStatement *ast); - virtual bool visit(AST::ForStatement *ast); - virtual bool visit(AST::IfStatement *ast); - virtual bool visit(AST::LabelledStatement *ast); - virtual bool visit(AST::LocalForEachStatement *ast); - virtual bool visit(AST::LocalForStatement *ast); - virtual bool visit(AST::ReturnStatement *ast); - virtual bool visit(AST::SwitchStatement *ast); - virtual bool visit(AST::ThrowStatement *ast); - virtual bool visit(AST::TryStatement *ast); - virtual bool visit(AST::VariableStatement *ast); - virtual bool visit(AST::WhileStatement *ast); - virtual bool visit(AST::WithStatement *ast); - - // ui object members - virtual bool visit(AST::UiArrayBinding *ast); - virtual bool visit(AST::UiObjectBinding *ast); - virtual bool visit(AST::UiObjectDefinition *ast); - virtual bool visit(AST::UiPublicMember *ast); - virtual bool visit(AST::UiScriptBinding *ast); - virtual bool visit(AST::UiSourceElement *ast); - - void throwSyntaxErrorOnEvalOrArgumentsInStrictMode(V4IR::Expr* expr, const AST::SourceLocation &loc); - - void throwSyntaxError(const AST::SourceLocation &loc, const QString &detail); - void throwReferenceError(const AST::SourceLocation &loc, const QString &detail); - -private: - QString _fileName; - Result _expr; - QString _property; - UiMember _uiMember; - V4IR::Module *_module; - V4IR::Function *_function; - V4IR::BasicBlock *_block; - V4IR::BasicBlock *_exitBlock; - V4IR::BasicBlock *_throwBlock; - unsigned _returnAddress; - Mode _mode; - Environment *_env; - Loop *_loop; - AST::LabelledStatement *_labelledStatement; - ScopeAndFinally *_scopeAndFinally; - QHash _envMap; - QHash _functionMap; - QV4::ExecutionContext *_context; - bool _strictMode; - ErrorHandler *_errorHandler; - - class ScanFunctions; -}; - -} - -QT_END_NAMESPACE - -#endif // QV4CODEGEN_P_H diff --git a/src/qml/qml/v4/qv4context.cpp b/src/qml/qml/v4/qv4context.cpp deleted file mode 100644 index ce947e51e8..0000000000 --- a/src/qml/qml/v4/qv4context.cpp +++ /dev/null @@ -1,615 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include -#include "qv4debugging_p.h" -#include -#include -#include -#include "qv4mm_p.h" -#include -#include "qv4function_p.h" - -using namespace QV4; - -DiagnosticMessage::DiagnosticMessage() - : offset(0) - , length(0) - , startLine(0) - , startColumn(0) - , type(0) - , next(0) -{} - -DiagnosticMessage::~DiagnosticMessage() -{ - delete next; -} - -String *DiagnosticMessage::buildFullMessage(ExecutionContext *ctx) const -{ - QString msg; - if (!fileName.isEmpty()) - msg = fileName + QLatin1Char(':'); - msg += QString::number(startLine) + QLatin1Char(':') + QString::number(startColumn) + QLatin1String(": "); - if (type == QV4::DiagnosticMessage::Error) - msg += QLatin1String("error"); - else - msg += QLatin1String("warning"); - msg += ": " + message; - - return ctx->engine->newString(msg); -} - -void ExecutionContext::createMutableBinding(String *name, bool deletable) -{ - - // find the right context to create the binding on - Object *activation = engine->globalObject; - ExecutionContext *ctx = this; - while (ctx) { - if (ctx->type >= Type_CallContext) { - CallContext *c = static_cast(ctx); - if (!c->activation) - c->activation = engine->newObject(); - activation = c->activation; - break; - } - ctx = ctx->outer; - } - - if (activation->__hasProperty__(name)) - return; - Property desc = Property::fromValue(Value::undefinedValue()); - PropertyAttributes attrs(Attr_Data); - attrs.setConfigurable(deletable); - activation->__defineOwnProperty__(this, name, desc, attrs); -} - -String * const *ExecutionContext::formals() const -{ - return type >= Type_CallContext ? static_cast(this)->function->formalParameterList : 0; -} - -unsigned int ExecutionContext::formalCount() const -{ - return type >= Type_CallContext ? static_cast(this)->function->formalParameterCount : 0; -} - -String * const *ExecutionContext::variables() const -{ - return type >= Type_CallContext ? static_cast(this)->function->varList : 0; -} - -unsigned int ExecutionContext::variableCount() const -{ - return type >= Type_CallContext ? static_cast(this)->function->varCount : 0; -} - - -void GlobalContext::initGlobalContext(ExecutionEngine *eng) -{ - initBaseContext(Type_GlobalContext, eng, /*parentContext*/0); - thisObject = Value::fromObject(eng->globalObject); - global = 0; -} - -void WithContext::initWithContext(ExecutionContext *p, Object *with) -{ - initBaseContext(Type_WithContext, p->engine, p); - thisObject = p->thisObject; - outer = p; - lookups = p->lookups; - - withObject = with; -} - -void CatchContext::initCatchContext(ExecutionContext *p, String *exceptionVarName, const Value &exceptionValue) -{ - initBaseContext(Type_CatchContext, p->engine, p); - strictMode = p->strictMode; - thisObject = p->thisObject; - outer = p; - lookups = p->lookups; - - this->exceptionVarName = exceptionVarName; - this->exceptionValue = exceptionValue; -} - -void CallContext::initCallContext(ExecutionContext *parentContext, FunctionObject *function, Value *_arguments, int _argumentCount, const Value &_thisObject) -{ - initBaseContext(Type_CallContext, parentContext->engine, parentContext); - - this->function = function; - this->arguments = _arguments; - this->argumentCount = _argumentCount; - this->thisObject = _thisObject; - - strictMode = function->strictMode; - marked = false; - outer = function->scope; -#ifndef QT_NO_DEBUG - assert(outer->next != (ExecutionContext *)0x1); -#endif - - activation = 0; - - if (function->function) - lookups = function->function->lookups; - - uint argc = argumentCount; - - locals = (Value *)(this + 1); - if (function->varCount) - std::fill(locals, locals + function->varCount, Value::undefinedValue()); - - if (needsOwnArguments()) { - Value *args = arguments; - argumentCount = qMax(argc, function->formalParameterCount); - arguments = locals + function->varCount; - if (argc) - ::memcpy(arguments, args, argc * sizeof(Value)); - if (argc < function->formalParameterCount) - std::fill(arguments + argc, arguments + function->formalParameterCount, Value::undefinedValue()); - - } - - if (function->usesArgumentsObject) { - ArgumentsObject *args = new (engine->memoryManager) ArgumentsObject(this, function->formalParameterCount, argc); - args->prototype = engine->objectPrototype; - activation = engine->newObject(); - Property desc = Property::fromValue(Value::fromObject(args)); - activation->__defineOwnProperty__(this, engine->id_arguments, desc, Attr_NotConfigurable); - } -} - -void CallContext::initQmlContext(ExecutionContext *parentContext, Object *qml, FunctionObject *function) -{ - initBaseContext(Type_QmlContext, parentContext->engine, parentContext); - - this->function = function; - this->arguments = 0; - this->argumentCount = 0; - this->thisObject = Value::undefinedValue(); - - strictMode = true; - marked = false; - this->outer = function->scope; -#ifndef QT_NO_DEBUG - assert(outer->next != (ExecutionContext *)0x1); -#endif - - activation = qml; - - lookups = function->function->lookups; - - locals = (Value *)(this + 1); - if (function->varCount) - std::fill(locals, locals + function->varCount, Value::undefinedValue()); -} - - -bool ExecutionContext::deleteProperty(String *name) -{ - bool hasWith = false; - for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer) { - if (ctx->type == Type_WithContext) { - hasWith = true; - WithContext *w = static_cast(ctx); - if (w->withObject->__hasProperty__(name)) - return w->withObject->deleteProperty(name); - } else if (ctx->type == Type_CatchContext) { - CatchContext *c = static_cast(ctx); - if (c->exceptionVarName->isEqualTo(name)) - return false; - } else if (ctx->type >= Type_CallContext) { - CallContext *c = static_cast(ctx); - FunctionObject *f = c->function; - if (f->needsActivation || hasWith) { - for (unsigned int i = 0; i < f->varCount; ++i) - if (f->varList[i]->isEqualTo(name)) - return false; - for (int i = (int)f->formalParameterCount - 1; i >= 0; --i) - if (f->formalParameterList[i]->isEqualTo(name)) - return false; - } - if (c->activation && c->activation->__hasProperty__(name)) - return c->activation->deleteProperty(name); - } else if (ctx->type == Type_GlobalContext) { - GlobalContext *g = static_cast(ctx); - if (g->global->__hasProperty__(name)) - return g->global->deleteProperty(name); - } - } - - if (strictMode) - throwSyntaxError(0); - return true; -} - -bool CallContext::needsOwnArguments() const -{ - return function->needsActivation || argumentCount < function->formalParameterCount; -} - -void ExecutionContext::mark() -{ - if (marked) - return; - marked = true; - - if (type != Type_SimpleCallContext && outer) - outer->mark(); - - thisObject.mark(); - - if (type >= Type_SimpleCallContext) { - QV4::CallContext *c = static_cast(this); - for (unsigned arg = 0, lastArg = c->argumentCount; arg < lastArg; ++arg) - c->arguments[arg].mark(); - if (type >= Type_CallContext) { - for (unsigned local = 0, lastLocal = c->variableCount(); local < lastLocal; ++local) - c->locals[local].mark(); - if (c->activation) - c->activation->mark(); - c->function->mark(); - } - } else if (type == Type_WithContext) { - WithContext *w = static_cast(this); - w->withObject->mark(); - } else if (type == Type_CatchContext) { - CatchContext *c = static_cast(this); - if (c->exceptionVarName) - c->exceptionVarName->mark(); - c->exceptionValue.mark(); - } else if (type == Type_GlobalContext) { - GlobalContext *g = static_cast(this); - g->global->mark(); - } -} - -void ExecutionContext::setProperty(String *name, const Value& value) -{ - for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer) { - if (ctx->type == Type_WithContext) { - Object *w = static_cast(ctx)->withObject; - if (w->__hasProperty__(name)) { - w->put(name, value); - return; - } - } else if (ctx->type == Type_CatchContext && static_cast(ctx)->exceptionVarName->isEqualTo(name)) { - static_cast(ctx)->exceptionValue = value; - return; - } else { - Object *activation = 0; - if (ctx->type >= Type_CallContext) { - CallContext *c = static_cast(ctx); - for (unsigned int i = 0; i < c->function->varCount; ++i) - if (c->function->varList[i]->isEqualTo(name)) { - c->locals[i] = value; - return; - } - for (int i = (int)c->function->formalParameterCount - 1; i >= 0; --i) - if (c->function->formalParameterList[i]->isEqualTo(name)) { - c->arguments[i] = value; - return; - } - activation = c->activation; - } else if (ctx->type == Type_GlobalContext) { - activation = static_cast(ctx)->global; - } - - if (activation && (ctx->type == Type_QmlContext || activation->__hasProperty__(name))) { - activation->put(name, value); - return; - } - } - } - if (strictMode || name->isEqualTo(engine->id_this)) - throwReferenceError(Value::fromString(name)); - engine->globalObject->put(name, value); -} - -Value ExecutionContext::getProperty(String *name) -{ - name->makeIdentifier(); - - if (name->isEqualTo(engine->id_this)) - return thisObject; - - bool hasWith = false; - bool hasCatchScope = false; - for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer) { - if (ctx->type == Type_WithContext) { - Object *w = static_cast(ctx)->withObject; - hasWith = true; - bool hasProperty = false; - Value v = w->get(name, &hasProperty); - if (hasProperty) { - return v; - } - continue; - } - - else if (ctx->type == Type_CatchContext) { - hasCatchScope = true; - CatchContext *c = static_cast(ctx); - if (c->exceptionVarName->isEqualTo(name)) - return c->exceptionValue; - } - - else if (ctx->type >= Type_CallContext) { - QV4::CallContext *c = static_cast(ctx); - FunctionObject *f = c->function; - if (f->needsActivation || hasWith || hasCatchScope) { - for (unsigned int i = 0; i < f->varCount; ++i) - if (f->varList[i]->isEqualTo(name)) - return c->locals[i]; - for (int i = (int)f->formalParameterCount - 1; i >= 0; --i) - if (f->formalParameterList[i]->isEqualTo(name)) - return c->arguments[i]; - } - if (c->activation) { - bool hasProperty = false; - Value v = c->activation->get(name, &hasProperty); - if (hasProperty) - return v; - } - if (f->function && f->function->isNamedExpression - && name->isEqualTo(f->function->name)) - return Value::fromObject(c->function); - } - - else if (ctx->type == Type_GlobalContext) { - GlobalContext *g = static_cast(ctx); - bool hasProperty = false; - Value v = g->global->get(name, &hasProperty); - if (hasProperty) - return v; - } - } - throwReferenceError(Value::fromString(name)); - return Value::undefinedValue(); -} - -Value ExecutionContext::getPropertyNoThrow(String *name) -{ - name->makeIdentifier(); - - if (name->isEqualTo(engine->id_this)) - return thisObject; - - bool hasWith = false; - bool hasCatchScope = false; - for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer) { - if (ctx->type == Type_WithContext) { - Object *w = static_cast(ctx)->withObject; - hasWith = true; - bool hasProperty = false; - Value v = w->get(name, &hasProperty); - if (hasProperty) { - return v; - } - continue; - } - - else if (ctx->type == Type_CatchContext) { - hasCatchScope = true; - CatchContext *c = static_cast(ctx); - if (c->exceptionVarName->isEqualTo(name)) - return c->exceptionValue; - } - - else if (ctx->type >= Type_CallContext) { - QV4::CallContext *c = static_cast(ctx); - FunctionObject *f = c->function; - if (f->needsActivation || hasWith || hasCatchScope) { - for (unsigned int i = 0; i < f->varCount; ++i) - if (f->varList[i]->isEqualTo(name)) - return c->locals[i]; - for (int i = (int)f->formalParameterCount - 1; i >= 0; --i) - if (f->formalParameterList[i]->isEqualTo(name)) - return c->arguments[i]; - } - if (c->activation) { - bool hasProperty = false; - Value v = c->activation->get(name, &hasProperty); - if (hasProperty) - return v; - } - if (f->function && f->function->isNamedExpression - && name->isEqualTo(f->function->name)) - return Value::fromObject(c->function); - } - - else if (ctx->type == Type_GlobalContext) { - GlobalContext *g = static_cast(ctx); - bool hasProperty = false; - Value v = g->global->get(name, &hasProperty); - if (hasProperty) - return v; - } - } - return Value::undefinedValue(); -} - -Value ExecutionContext::getPropertyAndBase(String *name, Object **base) -{ - *base = 0; - name->makeIdentifier(); - - if (name->isEqualTo(engine->id_this)) - return thisObject; - - bool hasWith = false; - bool hasCatchScope = false; - for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer) { - if (ctx->type == Type_WithContext) { - Object *w = static_cast(ctx)->withObject; - hasWith = true; - bool hasProperty = false; - Value v = w->get(name, &hasProperty); - if (hasProperty) { - *base = w; - return v; - } - continue; - } - - else if (ctx->type == Type_CatchContext) { - hasCatchScope = true; - CatchContext *c = static_cast(ctx); - if (c->exceptionVarName->isEqualTo(name)) - return c->exceptionValue; - } - - else if (ctx->type >= Type_CallContext) { - QV4::CallContext *c = static_cast(ctx); - FunctionObject *f = c->function; - if (f->needsActivation || hasWith || hasCatchScope) { - for (unsigned int i = 0; i < f->varCount; ++i) - if (f->varList[i]->isEqualTo(name)) - return c->locals[i]; - for (int i = (int)f->formalParameterCount - 1; i >= 0; --i) - if (f->formalParameterList[i]->isEqualTo(name)) - return c->arguments[i]; - } - if (c->activation) { - bool hasProperty = false; - Value v = c->activation->get(name, &hasProperty); - if (hasProperty) { - *base = c->activation; - return v; - } - } - if (f->function && f->function->isNamedExpression - && name->isEqualTo(f->function->name)) - return Value::fromObject(c->function); - } - - else if (ctx->type == Type_GlobalContext) { - GlobalContext *g = static_cast(ctx); - bool hasProperty = false; - Value v = g->global->get(name, &hasProperty); - if (hasProperty) - return v; - } - } - throwReferenceError(Value::fromString(name)); - return Value::undefinedValue(); -} - - - -void ExecutionContext::inplaceBitOp(String *name, const Value &value, BinOp op) -{ - Value lhs = getProperty(name); - Value result; - op(&result, lhs, value); - setProperty(name, result); -} - -void ExecutionContext::throwError(const Value &value) -{ - __qmljs_throw(this, value); -} - -void ExecutionContext::throwError(const QString &message) -{ - Value v = Value::fromString(this, message); - throwError(Value::fromObject(engine->newErrorObject(v))); -} - -void ExecutionContext::throwSyntaxError(DiagnosticMessage *message) -{ - throwError(Value::fromObject(engine->newSyntaxErrorObject(this, message))); -} - -void ExecutionContext::throwTypeError() -{ - throwError(Value::fromObject(engine->newTypeErrorObject(QStringLiteral("Type error")))); -} - -void ExecutionContext::throwTypeError(const QString &message) -{ - throwError(Value::fromObject(engine->newTypeErrorObject(message))); -} - -void ExecutionContext::throwUnimplemented(const QString &message) -{ - Value v = Value::fromString(this, QStringLiteral("Unimplemented ") + message); - throwError(Value::fromObject(engine->newErrorObject(v))); -} - -void ExecutionContext::throwReferenceError(Value value) -{ - String *s = value.toString(this); - QString msg = s->toQString() + QStringLiteral(" is not defined"); - throwError(Value::fromObject(engine->newReferenceErrorObject(msg))); -} - -void ExecutionContext::throwReferenceError(Value value, const QString &fileName, int line) -{ - String *s = value.toString(this); - QString msg = s->toQString() + QStringLiteral(" is not defined"); - throwError(Value::fromObject(engine->newReferenceErrorObject(msg, fileName, line))); -} - -void ExecutionContext::throwRangeError(Value value) -{ - String *s = value.toString(this); - QString msg = s->toQString() + QStringLiteral(" out of range"); - throwError(Value::fromObject(engine->newRangeErrorObject(msg))); -} - -void ExecutionContext::throwURIError(Value msg) -{ - throwError(Value::fromObject(engine->newURIErrorObject(msg))); -} - - -void SimpleCallContext::initSimpleCallContext(ExecutionEngine *engine) -{ - initBaseContext(Type_SimpleCallContext, engine, engine->current); - function = 0; - arguments = 0; - argumentCount = 0; -} diff --git a/src/qml/qml/v4/qv4context_p.h b/src/qml/qml/v4/qv4context_p.h deleted file mode 100644 index dfe02bdcc8..0000000000 --- a/src/qml/qml/v4/qv4context_p.h +++ /dev/null @@ -1,227 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QMLJS_ENVIRONMENT_H -#define QMLJS_ENVIRONMENT_H - -#include "qv4global_p.h" -#include "qv4runtime_p.h" - -QT_BEGIN_NAMESPACE - -namespace QV4 { - -struct Object; -struct ExecutionEngine; -struct DeclarativeEnvironment; -struct Lookup; - -struct Q_QML_EXPORT DiagnosticMessage -{ - enum { Error, Warning }; - - QString fileName; - quint32 offset; - quint32 length; - quint32 startLine; - unsigned startColumn: 31; - unsigned type: 1; - QString message; - DiagnosticMessage *next; - - DiagnosticMessage(); - ~DiagnosticMessage(); - String *buildFullMessage(ExecutionContext *ctx) const; -}; - -struct CallContext; - -struct Q_QML_EXPORT ExecutionContext -{ - enum Type { - Type_GlobalContext = 0x1, - Type_CatchContext = 0x2, - Type_WithContext = 0x3, - Type_SimpleCallContext = 0x4, - Type_CallContext = 0x5, - Type_QmlContext = 0x6 - }; - - Type type; - bool strictMode; - bool marked; - - Value thisObject; - - ExecutionEngine *engine; - ExecutionContext *parent; - ExecutionContext *outer; - Lookup *lookups; - ExecutionContext *next; // used in the GC - - struct EvalCode - { - Function *function; - EvalCode *next; - }; - EvalCode *currentEvalCode; - - const uchar **interpreterInstructionPointer; - - void initBaseContext(Type type, ExecutionEngine *engine, ExecutionContext *parentContext) - { - this->type = type; - strictMode = false; - marked = false; - thisObject = Value::undefinedValue(); - this->engine = engine; - parent = parentContext; - outer = 0; - lookups = 0; - currentEvalCode = 0; - interpreterInstructionPointer = 0; - } - - String * const *formals() const; - unsigned int formalCount() const; - String * const *variables() const; - unsigned int variableCount() const; - - void createMutableBinding(String *name, bool deletable); - - void Q_NORETURN throwError(const Value &value); - void Q_NORETURN throwError(const QString &message); - void Q_NORETURN throwSyntaxError(DiagnosticMessage *message); - void Q_NORETURN throwTypeError(); - void Q_NORETURN throwTypeError(const QString &message); - void Q_NORETURN throwReferenceError(Value value); - void Q_NORETURN throwReferenceError(Value value, const QString &fileName, int line); - void Q_NORETURN throwRangeError(Value value); - void Q_NORETURN throwURIError(Value msg); - void Q_NORETURN throwUnimplemented(const QString &message); - - void setProperty(String *name, const Value &value); - Value getProperty(String *name); - Value getPropertyNoThrow(String *name); - Value getPropertyAndBase(String *name, Object **base); - void inplaceBitOp(String *name, const QV4::Value &value, BinOp op); - bool deleteProperty(String *name); - - inline Value argument(unsigned int index = 0); - - void mark(); - - inline CallContext *asCallContext(); - inline const CallContext *asCallContext() const; -}; - -struct SimpleCallContext : public ExecutionContext -{ - void initSimpleCallContext(ExecutionEngine *engine); - FunctionObject *function; - Value *arguments; - unsigned int argumentCount; -}; - -struct CallContext : public SimpleCallContext -{ - void initCallContext(ExecutionContext *parentContext, FunctionObject *function, Value *args, int argc, - const Value &thisObject); - void initQmlContext(ExecutionContext *parentContext, Object *qml, QV4::FunctionObject *function); - bool needsOwnArguments() const; - - Value *locals; - Object *activation; -}; - -struct GlobalContext : public ExecutionContext -{ - void initGlobalContext(ExecutionEngine *e); - - Object *global; -}; - -struct CatchContext : public ExecutionContext -{ - void initCatchContext(ExecutionContext *p, String *exceptionVarName, const QV4::Value &exceptionValue); - - String *exceptionVarName; - Value exceptionValue; -}; - -struct WithContext : public ExecutionContext -{ - Object *withObject; - - void initWithContext(ExecutionContext *p, Object *with); -}; - -inline Value ExecutionContext::argument(unsigned int index) -{ - if (type >= Type_SimpleCallContext) { - CallContext *ctx = static_cast(this); - if (index < ctx->argumentCount) - return ctx->arguments[index]; - } - return Value::undefinedValue(); -} - -inline CallContext *ExecutionContext::asCallContext() -{ - return type >= Type_CallContext ? static_cast(this) : 0; -} - -inline const CallContext *ExecutionContext::asCallContext() const -{ - return type >= Type_CallContext ? static_cast(this) : 0; -} - -/* Function *f, int argc */ -#define requiredMemoryForExecutionContect(f, argc) \ - sizeof(CallContext) + sizeof(Value) * (f->varCount + qMax((uint)argc, f->formalParameterCount)) -#define requiredMemoryForQmlExecutionContect(f) \ - sizeof(CallContext) + sizeof(Value) * (f->locals.size()) -#define stackContextSize (sizeof(CallContext) + 32*sizeof(Value)) - -} // namespace QV4 - -QT_END_NAMESPACE - -#endif diff --git a/src/qml/qml/v4/qv4dateobject.cpp b/src/qml/qml/v4/qv4dateobject.cpp deleted file mode 100644 index 3cf6cb1aeb..0000000000 --- a/src/qml/qml/v4/qv4dateobject.cpp +++ /dev/null @@ -1,1316 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - - -#include "qv4dateobject_p.h" -#include "qv4objectproto_p.h" -#include "qv4mm_p.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include - -#ifdef Q_OS_WIN -# include -#else -# ifndef Q_OS_VXWORKS -# include -# else -# include "qplatformdefs.h" -# endif -#endif - -using namespace QV4; - -static const double HoursPerDay = 24.0; -static const double MinutesPerHour = 60.0; -static const double SecondsPerMinute = 60.0; -static const double msPerSecond = 1000.0; -static const double msPerMinute = 60000.0; -static const double msPerHour = 3600000.0; -static const double msPerDay = 86400000.0; - -static double LocalTZA = 0.0; // initialized at startup - -static inline double TimeWithinDay(double t) -{ - double r = ::fmod(t, msPerDay); - return (r >= 0) ? r : r + msPerDay; -} - -static inline int HourFromTime(double t) -{ - int r = int(::fmod(::floor(t / msPerHour), HoursPerDay)); - return (r >= 0) ? r : r + int(HoursPerDay); -} - -static inline int MinFromTime(double t) -{ - int r = int(::fmod(::floor(t / msPerMinute), MinutesPerHour)); - return (r >= 0) ? r : r + int(MinutesPerHour); -} - -static inline int SecFromTime(double t) -{ - int r = int(::fmod(::floor(t / msPerSecond), SecondsPerMinute)); - return (r >= 0) ? r : r + int(SecondsPerMinute); -} - -static inline int msFromTime(double t) -{ - int r = int(::fmod(t, msPerSecond)); - return (r >= 0) ? r : r + int(msPerSecond); -} - -static inline double Day(double t) -{ - return ::floor(t / msPerDay); -} - -static inline double DaysInYear(double y) -{ - if (::fmod(y, 4)) - return 365; - - else if (::fmod(y, 100)) - return 366; - - else if (::fmod(y, 400)) - return 365; - - return 366; -} - -static inline double DayFromYear(double y) -{ - return 365 * (y - 1970) - + ::floor((y - 1969) / 4) - - ::floor((y - 1901) / 100) - + ::floor((y - 1601) / 400); -} - -static inline double TimeFromYear(double y) -{ - return msPerDay * DayFromYear(y); -} - -static inline double YearFromTime(double t) -{ - int y = 1970; - y += (int) ::floor(t / (msPerDay * 365.2425)); - - double t2 = TimeFromYear(y); - return (t2 > t) ? y - 1 : ((t2 + msPerDay * DaysInYear(y)) <= t) ? y + 1 : y; -} - -static inline bool InLeapYear(double t) -{ - double x = DaysInYear(YearFromTime(t)); - if (x == 365) - return 0; - - assert(x == 366); - return 1; -} - -static inline double DayWithinYear(double t) -{ - return Day(t) - DayFromYear(YearFromTime(t)); -} - -static inline double MonthFromTime(double t) -{ - double d = DayWithinYear(t); - double l = InLeapYear(t); - - if (d < 31.0) - return 0; - - else if (d < 59.0 + l) - return 1; - - else if (d < 90.0 + l) - return 2; - - else if (d < 120.0 + l) - return 3; - - else if (d < 151.0 + l) - return 4; - - else if (d < 181.0 + l) - return 5; - - else if (d < 212.0 + l) - return 6; - - else if (d < 243.0 + l) - return 7; - - else if (d < 273.0 + l) - return 8; - - else if (d < 304.0 + l) - return 9; - - else if (d < 334.0 + l) - return 10; - - else if (d < 365.0 + l) - return 11; - - return qSNaN(); // ### assert? -} - -static inline double DateFromTime(double t) -{ - int m = (int) Value::toInteger(MonthFromTime(t)); - double d = DayWithinYear(t); - double l = InLeapYear(t); - - switch (m) { - case 0: return d + 1.0; - case 1: return d - 30.0; - case 2: return d - 58.0 - l; - case 3: return d - 89.0 - l; - case 4: return d - 119.0 - l; - case 5: return d - 150.0 - l; - case 6: return d - 180.0 - l; - case 7: return d - 211.0 - l; - case 8: return d - 242.0 - l; - case 9: return d - 272.0 - l; - case 10: return d - 303.0 - l; - case 11: return d - 333.0 - l; - } - - return qSNaN(); // ### assert -} - -static inline double WeekDay(double t) -{ - double r = ::fmod (Day(t) + 4.0, 7.0); - return (r >= 0) ? r : r + 7.0; -} - - -static inline double MakeTime(double hour, double min, double sec, double ms) -{ - return ((hour * MinutesPerHour + min) * SecondsPerMinute + sec) * msPerSecond + ms; -} - -static inline double DayFromMonth(double month, double leap) -{ - switch ((int) month) { - case 0: return 0; - case 1: return 31.0; - case 2: return 59.0 + leap; - case 3: return 90.0 + leap; - case 4: return 120.0 + leap; - case 5: return 151.0 + leap; - case 6: return 181.0 + leap; - case 7: return 212.0 + leap; - case 8: return 243.0 + leap; - case 9: return 273.0 + leap; - case 10: return 304.0 + leap; - case 11: return 334.0 + leap; - } - - return qSNaN(); // ### assert? -} - -static double MakeDay(double year, double month, double day) -{ - year += ::floor(month / 12.0); - - month = ::fmod(month, 12.0); - if (month < 0) - month += 12.0; - - double d = DayFromYear(year); - bool leap = InLeapYear(d*msPerDay); - - d += DayFromMonth(month, leap); - d += day - 1; - - return d; -} - -static inline double MakeDate(double day, double time) -{ - return day * msPerDay + time; -} - -static inline double DaylightSavingTA(double t) -{ - struct tm tmtm; -#if defined(_MSC_VER) && _MSC_VER >= 1400 - __time64_t tt = (__time64_t)(t / msPerSecond); - // _localtime_64_s returns non-zero on failure - if (_localtime64_s(&tmtm, &tt) != 0) -#else - long int tt = (long int)(t / msPerSecond); - if (!localtime_r((const time_t*) &tt, &tmtm)) -#endif - return 0; - return (tmtm.tm_isdst > 0) ? msPerHour : 0; -} - -static inline double LocalTime(double t) -{ - return t + LocalTZA + DaylightSavingTA(t); -} - -static inline double UTC(double t) -{ - return t - LocalTZA - DaylightSavingTA(t - LocalTZA); -} - -static inline double currentTime() -{ -#ifndef Q_OS_WIN - struct timeval tv; - - gettimeofday(&tv, 0); - return ::floor(tv.tv_sec * msPerSecond + (tv.tv_usec / 1000.0)); -#else - SYSTEMTIME st; - GetSystemTime(&st); - FILETIME ft; - SystemTimeToFileTime(&st, &ft); - LARGE_INTEGER li; - li.LowPart = ft.dwLowDateTime; - li.HighPart = ft.dwHighDateTime; - return double(li.QuadPart - Q_INT64_C(116444736000000000)) / 10000.0; -#endif -} - -static inline double TimeClip(double t) -{ - if (! qIsFinite(t) || fabs(t) > 8.64e15) - return qSNaN(); - return Value::toInteger(t); -} - -static inline double ParseString(const QString &s) -{ - // first try the format defined in 15.9.1.15, only if that fails fall back to - // QDateTime for parsing - - // the define string format is YYYY-MM-DDTHH:mm:ss.sssZ - // It can be date or time only, and the second and later components - // of both fields are optional - // and extended syntax for negative and large positive years exists: +/-YYYYYY - - enum Format { - Year, - Month, - Day, - Hour, - Minute, - Second, - MilliSecond, - TimezoneHour, - TimezoneMinute, - Done - }; - - const QChar *ch = s.constData(); - const QChar *end = ch + s.length(); - - uint format = Year; - int current = 0; - int currentSize = 0; - bool extendedYear = false; - - int yearSign = 1; - int year = 0; - int month = 0; - int day = 1; - int hour = 0; - int minute = 0; - int second = 0; - int msec = 0; - int offsetSign = 1; - int offset = 0; - - bool error = false; - if (*ch == '+' || *ch == '-') { - extendedYear = true; - if (*ch == '-') - yearSign = -1; - ++ch; - } - while (ch <= end) { - if (*ch >= '0' && *ch <= '9') { - current *= 10; - current += ch->unicode() - '0'; - ++currentSize; - } else { // other char, delimits field - switch (format) { - case Year: - year = current; - if (extendedYear) - error = (currentSize != 6); - else - error = (currentSize != 4); - break; - case Month: - month = current - 1; - error = (currentSize != 2) || month > 11; - break; - case Day: - day = current; - error = (currentSize != 2) || day > 31; - break; - case Hour: - hour = current; - error = (currentSize != 2) || hour > 24; - break; - case Minute: - minute = current; - error = (currentSize != 2) || minute > 60; - break; - case Second: - second = current; - error = (currentSize != 2) || second > 60; - break; - case MilliSecond: - msec = current; - error = (currentSize != 3); - break; - case TimezoneHour: - offset = current*60; - error = (currentSize != 2) || offset > 23*60; - break; - case TimezoneMinute: - offset += current; - error = (currentSize != 2) || current >= 60; - break; - } - if (*ch == 'T') { - if (format >= Hour) - error = true; - format = Hour; - } else if (*ch == '-') { - if (format < Day) - ++format; - else if (format < Minute) - error = true; - else if (format >= TimezoneHour) - error = true; - else { - offsetSign = -1; - format = TimezoneHour; - } - } else if (*ch == ':') { - if (format != Hour && format != Minute && format != TimezoneHour) - error = true; - ++format; - } else if (*ch == '.') { - if (format != Second) - error = true; - ++format; - } else if (*ch == '+') { - if (format < Minute || format >= TimezoneHour) - error = true; - format = TimezoneHour; - } else if (*ch == 'Z' || *ch == 0) { - format = Done; - } - current = 0; - currentSize = 0; - } - if (error || format == Done) - break; - ++ch; - } - - if (!error) { - double t = MakeDate(MakeDay(year * yearSign, month, day), MakeTime(hour, minute, second, msec)); - t -= offset * offsetSign * 60 * 1000; - return t; - } - - QDateTime dt = QDateTime::fromString(s, Qt::TextDate); - if (!dt.isValid()) - dt = QDateTime::fromString(s, Qt::ISODate); - if (!dt.isValid()) { - QStringList formats; - formats << QStringLiteral("M/d/yyyy") - << QStringLiteral("M/d/yyyy hh:mm") - << QStringLiteral("M/d/yyyy hh:mm A") - - << QStringLiteral("M/d/yyyy, hh:mm") - << QStringLiteral("M/d/yyyy, hh:mm A") - - << QStringLiteral("MMM d yyyy") - << QStringLiteral("MMM d yyyy hh:mm") - << QStringLiteral("MMM d yyyy hh:mm:ss") - << QStringLiteral("MMM d yyyy, hh:mm") - << QStringLiteral("MMM d yyyy, hh:mm:ss") - - << QStringLiteral("MMMM d yyyy") - << QStringLiteral("MMMM d yyyy hh:mm") - << QStringLiteral("MMMM d yyyy hh:mm:ss") - << QStringLiteral("MMMM d yyyy, hh:mm") - << QStringLiteral("MMMM d yyyy, hh:mm:ss") - - << QStringLiteral("MMM d, yyyy") - << QStringLiteral("MMM d, yyyy hh:mm") - << QStringLiteral("MMM d, yyyy hh:mm:ss") - - << QStringLiteral("MMMM d, yyyy") - << QStringLiteral("MMMM d, yyyy hh:mm") - << QStringLiteral("MMMM d, yyyy hh:mm:ss") - - << QStringLiteral("d MMM yyyy") - << QStringLiteral("d MMM yyyy hh:mm") - << QStringLiteral("d MMM yyyy hh:mm:ss") - << QStringLiteral("d MMM yyyy, hh:mm") - << QStringLiteral("d MMM yyyy, hh:mm:ss") - - << QStringLiteral("d MMMM yyyy") - << QStringLiteral("d MMMM yyyy hh:mm") - << QStringLiteral("d MMMM yyyy hh:mm:ss") - << QStringLiteral("d MMMM yyyy, hh:mm") - << QStringLiteral("d MMMM yyyy, hh:mm:ss") - - << QStringLiteral("d MMM, yyyy") - << QStringLiteral("d MMM, yyyy hh:mm") - << QStringLiteral("d MMM, yyyy hh:mm:ss") - - << QStringLiteral("d MMMM, yyyy") - << QStringLiteral("d MMMM, yyyy hh:mm") - << QStringLiteral("d MMMM, yyyy hh:mm:ss"); - - for (int i = 0; i < formats.size(); ++i) { - dt = QDateTime::fromString(s, formats.at(i)); - if (dt.isValid()) - break; - } - } - if (!dt.isValid()) - return qSNaN(); - return dt.toMSecsSinceEpoch(); -} - -/*! - \internal - - Converts the ECMA Date value \tt (in UTC form) to QDateTime - according to \a spec. -*/ -static inline QDateTime ToDateTime(double t, Qt::TimeSpec spec) -{ - if (std::isnan(t)) - return QDateTime(); - if (spec == Qt::LocalTime) - t = LocalTime(t); - int year = int(YearFromTime(t)); - int month = int(MonthFromTime(t) + 1); - int day = int(DateFromTime(t)); - int hours = HourFromTime(t); - int mins = MinFromTime(t); - int secs = SecFromTime(t); - int ms = msFromTime(t); - return QDateTime(QDate(year, month, day), QTime(hours, mins, secs, ms), spec); -} - -static inline QString ToString(double t) -{ - if (std::isnan(t)) - return QStringLiteral("Invalid Date"); - QString str = ToDateTime(t, Qt::LocalTime).toString() + QStringLiteral(" GMT"); - double tzoffset = LocalTZA + DaylightSavingTA(t); - if (tzoffset) { - int hours = static_cast(::fabs(tzoffset) / 1000 / 60 / 60); - int mins = int(::fabs(tzoffset) / 1000 / 60) % 60; - str.append(QLatin1Char((tzoffset > 0) ? '+' : '-')); - if (hours < 10) - str.append(QLatin1Char('0')); - str.append(QString::number(hours)); - if (mins < 10) - str.append(QLatin1Char('0')); - str.append(QString::number(mins)); - } - return str; -} - -static inline QString ToUTCString(double t) -{ - if (std::isnan(t)) - return QStringLiteral("Invalid Date"); - return ToDateTime(t, Qt::UTC).toString() + QStringLiteral(" GMT"); -} - -static inline QString ToDateString(double t) -{ - return ToDateTime(t, Qt::LocalTime).date().toString(); -} - -static inline QString ToTimeString(double t) -{ - return ToDateTime(t, Qt::LocalTime).time().toString(); -} - -static inline QString ToLocaleString(double t) -{ - return ToDateTime(t, Qt::LocalTime).toString(Qt::LocaleDate); -} - -static inline QString ToLocaleDateString(double t) -{ - return ToDateTime(t, Qt::LocalTime).date().toString(Qt::LocaleDate); -} - -static inline QString ToLocaleTimeString(double t) -{ - return ToDateTime(t, Qt::LocalTime).time().toString(Qt::LocaleDate); -} - -static double getLocalTZA() -{ -#ifndef Q_OS_WIN - struct tm t; - time_t curr; - time(&curr); - localtime_r(&curr, &t); - time_t locl = mktime(&t); - gmtime_r(&curr, &t); - time_t globl = mktime(&t); - return double(locl - globl) * 1000.0; -#else - TIME_ZONE_INFORMATION tzInfo; - GetTimeZoneInformation(&tzInfo); - return -tzInfo.Bias * 60.0 * 1000.0; -#endif -} - -DateObject::DateObject(ExecutionEngine *engine, const QDateTime &date) - : Object(engine) -{ - type = Type_DateObject; - value = Value::fromDouble(date.toMSecsSinceEpoch()); -} - -QDateTime DateObject::toQDateTime() const -{ - return ToDateTime(value.asDouble(), Qt::LocalTime); -} - -DEFINE_MANAGED_VTABLE(DateCtor); - -DateCtor::DateCtor(ExecutionContext *scope) - : FunctionObject(scope, scope->engine->newIdentifier(QStringLiteral("Date"))) -{ - vtbl = &static_vtbl; -} - -Value DateCtor::construct(Managed *m, Value *args, int argc) -{ - double t = 0; - - if (argc == 0) - t = currentTime(); - - else if (argc == 1) { - Value arg = args[0]; - if (DateObject *d = arg.asDateObject()) - arg = d->value; - else - arg = __qmljs_to_primitive(arg, PREFERREDTYPE_HINT); - - if (arg.isString()) - t = ParseString(arg.stringValue()->toQString()); - else - t = TimeClip(arg.toNumber()); - } - - else { // argc > 1 - double year = args[0].toNumber(); - double month = args[1].toNumber(); - double day = argc >= 3 ? args[2].toNumber() : 1; - double hours = argc >= 4 ? args[3].toNumber() : 0; - double mins = argc >= 5 ? args[4].toNumber() : 0; - double secs = argc >= 6 ? args[5].toNumber() : 0; - double ms = argc >= 7 ? args[6].toNumber() : 0; - if (year >= 0 && year <= 99) - year += 1900; - t = MakeDate(MakeDay(year, month, day), MakeTime(hours, mins, secs, ms)); - t = TimeClip(UTC(t)); - } - - Object *d = m->engine()->newDateObject(Value::fromDouble(t)); - return Value::fromObject(d); -} - -Value DateCtor::call(Managed *m, const Value &, Value *, int) -{ - double t = currentTime(); - return Value::fromString(m->engine()->current, ToString(t)); -} - -void DatePrototype::init(ExecutionContext *ctx, const Value &ctor) -{ - ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); - ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(7)); - LocalTZA = getLocalTZA(); - - ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("parse"), method_parse, 1); - ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("UTC"), method_UTC, 7); - ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("now"), method_now, 0); - - defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); - defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); - defineDefaultProperty(ctx, QStringLiteral("toDateString"), method_toDateString, 0); - defineDefaultProperty(ctx, QStringLiteral("toTimeString"), method_toTimeString, 0); - defineDefaultProperty(ctx, QStringLiteral("toLocaleString"), method_toLocaleString, 0); - defineDefaultProperty(ctx, QStringLiteral("toLocaleDateString"), method_toLocaleDateString, 0); - defineDefaultProperty(ctx, QStringLiteral("toLocaleTimeString"), method_toLocaleTimeString, 0); - defineDefaultProperty(ctx, QStringLiteral("valueOf"), method_valueOf, 0); - defineDefaultProperty(ctx, QStringLiteral("getTime"), method_getTime, 0); - defineDefaultProperty(ctx, QStringLiteral("getYear"), method_getYear, 0); - defineDefaultProperty(ctx, QStringLiteral("getFullYear"), method_getFullYear, 0); - defineDefaultProperty(ctx, QStringLiteral("getUTCFullYear"), method_getUTCFullYear, 0); - defineDefaultProperty(ctx, QStringLiteral("getMonth"), method_getMonth, 0); - defineDefaultProperty(ctx, QStringLiteral("getUTCMonth"), method_getUTCMonth, 0); - defineDefaultProperty(ctx, QStringLiteral("getDate"), method_getDate, 0); - defineDefaultProperty(ctx, QStringLiteral("getUTCDate"), method_getUTCDate, 0); - defineDefaultProperty(ctx, QStringLiteral("getDay"), method_getDay, 0); - defineDefaultProperty(ctx, QStringLiteral("getUTCDay"), method_getUTCDay, 0); - defineDefaultProperty(ctx, QStringLiteral("getHours"), method_getHours, 0); - defineDefaultProperty(ctx, QStringLiteral("getUTCHours"), method_getUTCHours, 0); - defineDefaultProperty(ctx, QStringLiteral("getMinutes"), method_getMinutes, 0); - defineDefaultProperty(ctx, QStringLiteral("getUTCMinutes"), method_getUTCMinutes, 0); - defineDefaultProperty(ctx, QStringLiteral("getSeconds"), method_getSeconds, 0); - defineDefaultProperty(ctx, QStringLiteral("getUTCSeconds"), method_getUTCSeconds, 0); - defineDefaultProperty(ctx, QStringLiteral("getMilliseconds"), method_getMilliseconds, 0); - defineDefaultProperty(ctx, QStringLiteral("getUTCMilliseconds"), method_getUTCMilliseconds, 0); - defineDefaultProperty(ctx, QStringLiteral("getTimezoneOffset"), method_getTimezoneOffset, 0); - defineDefaultProperty(ctx, QStringLiteral("setTime"), method_setTime, 1); - defineDefaultProperty(ctx, QStringLiteral("setMilliseconds"), method_setMilliseconds, 1); - defineDefaultProperty(ctx, QStringLiteral("setUTCMilliseconds"), method_setUTCMilliseconds, 1); - defineDefaultProperty(ctx, QStringLiteral("setSeconds"), method_setSeconds, 2); - defineDefaultProperty(ctx, QStringLiteral("setUTCSeconds"), method_setUTCSeconds, 2); - defineDefaultProperty(ctx, QStringLiteral("setMinutes"), method_setMinutes, 3); - defineDefaultProperty(ctx, QStringLiteral("setUTCMinutes"), method_setUTCMinutes, 3); - defineDefaultProperty(ctx, QStringLiteral("setHours"), method_setHours, 4); - defineDefaultProperty(ctx, QStringLiteral("setUTCHours"), method_setUTCHours, 4); - defineDefaultProperty(ctx, QStringLiteral("setDate"), method_setDate, 1); - defineDefaultProperty(ctx, QStringLiteral("setUTCDate"), method_setUTCDate, 1); - defineDefaultProperty(ctx, QStringLiteral("setMonth"), method_setMonth, 2); - defineDefaultProperty(ctx, QStringLiteral("setUTCMonth"), method_setUTCMonth, 2); - defineDefaultProperty(ctx, QStringLiteral("setYear"), method_setYear, 1); - defineDefaultProperty(ctx, QStringLiteral("setFullYear"), method_setFullYear, 3); - defineDefaultProperty(ctx, QStringLiteral("setUTCFullYear"), method_setUTCFullYear, 3); - defineDefaultProperty(ctx, QStringLiteral("toUTCString"), method_toUTCString, 0); - defineDefaultProperty(ctx, QStringLiteral("toGMTString"), method_toUTCString, 0); - defineDefaultProperty(ctx, QStringLiteral("toISOString"), method_toISOString, 0); - defineDefaultProperty(ctx, QStringLiteral("toJSON"), method_toJSON, 1); -} - -double DatePrototype::getThisDate(ExecutionContext *ctx) -{ - if (DateObject *thisObject = ctx->thisObject.asDateObject()) - return thisObject->value.asDouble(); - else { - ctx->throwTypeError(); - return 0; - } -} - -Value DatePrototype::method_parse(SimpleCallContext *ctx) -{ - return Value::fromDouble(ParseString(ctx->argument(0).toString(ctx)->toQString())); -} - -Value DatePrototype::method_UTC(SimpleCallContext *ctx) -{ - const int numArgs = ctx->argumentCount; - if (numArgs >= 2) { - double year = ctx->argument(0).toNumber(); - double month = ctx->argument(1).toNumber(); - double day = numArgs >= 3 ? ctx->argument(2).toNumber() : 1; - double hours = numArgs >= 4 ? ctx->argument(3).toNumber() : 0; - double mins = numArgs >= 5 ? ctx->argument(4).toNumber() : 0; - double secs = numArgs >= 6 ? ctx->argument(5).toNumber() : 0; - double ms = numArgs >= 7 ? ctx->argument(6).toNumber() : 0; - if (year >= 0 && year <= 99) - year += 1900; - double t = MakeDate(MakeDay(year, month, day), - MakeTime(hours, mins, secs, ms)); - return Value::fromDouble(TimeClip(t)); - } - return Value::undefinedValue(); -} - -Value DatePrototype::method_now(SimpleCallContext *ctx) -{ - Q_UNUSED(ctx); - double t = currentTime(); - return Value::fromDouble(t); -} - -Value DatePrototype::method_toString(SimpleCallContext *ctx) -{ - double t = getThisDate(ctx); - return Value::fromString(ctx, ToString(t)); -} - -Value DatePrototype::method_toDateString(SimpleCallContext *ctx) -{ - double t = getThisDate(ctx); - return Value::fromString(ctx, ToDateString(t)); -} - -Value DatePrototype::method_toTimeString(SimpleCallContext *ctx) -{ - double t = getThisDate(ctx); - return Value::fromString(ctx, ToTimeString(t)); -} - -Value DatePrototype::method_toLocaleString(SimpleCallContext *ctx) -{ - double t = getThisDate(ctx); - return Value::fromString(ctx, ToLocaleString(t)); -} - -Value DatePrototype::method_toLocaleDateString(SimpleCallContext *ctx) -{ - double t = getThisDate(ctx); - return Value::fromString(ctx, ToLocaleDateString(t)); -} - -Value DatePrototype::method_toLocaleTimeString(SimpleCallContext *ctx) -{ - double t = getThisDate(ctx); - return Value::fromString(ctx, ToLocaleTimeString(t)); -} - -Value DatePrototype::method_valueOf(SimpleCallContext *ctx) -{ - double t = getThisDate(ctx); - return Value::fromDouble(t); -} - -Value DatePrototype::method_getTime(SimpleCallContext *ctx) -{ - double t = getThisDate(ctx); - return Value::fromDouble(t); -} - -Value DatePrototype::method_getYear(SimpleCallContext *ctx) -{ - double t = getThisDate(ctx); - if (! std::isnan(t)) - t = YearFromTime(LocalTime(t)) - 1900; - return Value::fromDouble(t); -} - -Value DatePrototype::method_getFullYear(SimpleCallContext *ctx) -{ - double t = getThisDate(ctx); - if (! std::isnan(t)) - t = YearFromTime(LocalTime(t)); - return Value::fromDouble(t); -} - -Value DatePrototype::method_getUTCFullYear(SimpleCallContext *ctx) -{ - double t = getThisDate(ctx); - if (! std::isnan(t)) - t = YearFromTime(t); - return Value::fromDouble(t); -} - -Value DatePrototype::method_getMonth(SimpleCallContext *ctx) -{ - double t = getThisDate(ctx); - if (! std::isnan(t)) - t = MonthFromTime(LocalTime(t)); - return Value::fromDouble(t); -} - -Value DatePrototype::method_getUTCMonth(SimpleCallContext *ctx) -{ - double t = getThisDate(ctx); - if (! std::isnan(t)) - t = MonthFromTime(t); - return Value::fromDouble(t); -} - -Value DatePrototype::method_getDate(SimpleCallContext *ctx) -{ - double t = getThisDate(ctx); - if (! std::isnan(t)) - t = DateFromTime(LocalTime(t)); - return Value::fromDouble(t); -} - -Value DatePrototype::method_getUTCDate(SimpleCallContext *ctx) -{ - double t = getThisDate(ctx); - if (! std::isnan(t)) - t = DateFromTime(t); - return Value::fromDouble(t); -} - -Value DatePrototype::method_getDay(SimpleCallContext *ctx) -{ - double t = getThisDate(ctx); - if (! std::isnan(t)) - t = WeekDay(LocalTime(t)); - return Value::fromDouble(t); -} - -Value DatePrototype::method_getUTCDay(SimpleCallContext *ctx) -{ - double t = getThisDate(ctx); - if (! std::isnan(t)) - t = WeekDay(t); - return Value::fromDouble(t); -} - -Value DatePrototype::method_getHours(SimpleCallContext *ctx) -{ - double t = getThisDate(ctx); - if (! std::isnan(t)) - t = HourFromTime(LocalTime(t)); - return Value::fromDouble(t); -} - -Value DatePrototype::method_getUTCHours(SimpleCallContext *ctx) -{ - double t = getThisDate(ctx); - if (! std::isnan(t)) - t = HourFromTime(t); - return Value::fromDouble(t); -} - -Value DatePrototype::method_getMinutes(SimpleCallContext *ctx) -{ - double t = getThisDate(ctx); - if (! std::isnan(t)) - t = MinFromTime(LocalTime(t)); - return Value::fromDouble(t); -} - -Value DatePrototype::method_getUTCMinutes(SimpleCallContext *ctx) -{ - double t = getThisDate(ctx); - if (! std::isnan(t)) - t = MinFromTime(t); - return Value::fromDouble(t); -} - -Value DatePrototype::method_getSeconds(SimpleCallContext *ctx) -{ - double t = getThisDate(ctx); - if (! std::isnan(t)) - t = SecFromTime(LocalTime(t)); - return Value::fromDouble(t); -} - -Value DatePrototype::method_getUTCSeconds(SimpleCallContext *ctx) -{ - double t = getThisDate(ctx); - if (! std::isnan(t)) - t = SecFromTime(t); - return Value::fromDouble(t); -} - -Value DatePrototype::method_getMilliseconds(SimpleCallContext *ctx) -{ - double t = getThisDate(ctx); - if (! std::isnan(t)) - t = msFromTime(LocalTime(t)); - return Value::fromDouble(t); -} - -Value DatePrototype::method_getUTCMilliseconds(SimpleCallContext *ctx) -{ - double t = getThisDate(ctx); - if (! std::isnan(t)) - t = msFromTime(t); - return Value::fromDouble(t); -} - -Value DatePrototype::method_getTimezoneOffset(SimpleCallContext *ctx) -{ - double t = getThisDate(ctx); - if (! std::isnan(t)) - t = (t - LocalTime(t)) / msPerMinute; - return Value::fromDouble(t); -} - -Value DatePrototype::method_setTime(SimpleCallContext *ctx) -{ - DateObject *self = ctx->thisObject.asDateObject(); - if (!self) - ctx->throwTypeError(); - - self->value.setDouble(TimeClip(ctx->argument(0).toNumber())); - return self->value; -} - -Value DatePrototype::method_setMilliseconds(SimpleCallContext *ctx) -{ - DateObject *self = ctx->thisObject.asDateObject(); - if (!self) - ctx->throwTypeError(); - - double t = LocalTime(self->value.asDouble()); - double ms = ctx->argument(0).toNumber(); - self->value.setDouble(TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms))))); - return self->value; -} - -Value DatePrototype::method_setUTCMilliseconds(SimpleCallContext *ctx) -{ - DateObject *self = ctx->thisObject.asDateObject(); - if (!self) - ctx->throwTypeError(); - - double t = self->value.asDouble(); - double ms = ctx->argument(0).toNumber(); - self->value.setDouble(TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms))))); - return self->value; -} - -Value DatePrototype::method_setSeconds(SimpleCallContext *ctx) -{ - DateObject *self = ctx->thisObject.asDateObject(); - if (!self) - ctx->throwTypeError(); - - double t = LocalTime(self->value.asDouble()); - double sec = ctx->argument(0).toNumber(); - double ms = (ctx->argumentCount < 2) ? msFromTime(t) : ctx->argument(1).toNumber(); - t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms)))); - self->value.setDouble(t); - return self->value; -} - -Value DatePrototype::method_setUTCSeconds(SimpleCallContext *ctx) -{ - DateObject *self = ctx->thisObject.asDateObject(); - if (!self) - ctx->throwTypeError(); - - double t = self->value.asDouble(); - double sec = ctx->argument(0).toNumber(); - double ms = (ctx->argumentCount < 2) ? msFromTime(t) : ctx->argument(1).toNumber(); - t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms)))); - self->value.setDouble(t); - return self->value; -} - -Value DatePrototype::method_setMinutes(SimpleCallContext *ctx) -{ - DateObject *self = ctx->thisObject.asDateObject(); - if (!self) - ctx->throwTypeError(); - - double t = LocalTime(self->value.asDouble()); - double min = ctx->argument(0).toNumber(); - double sec = (ctx->argumentCount < 2) ? SecFromTime(t) : ctx->argument(1).toNumber(); - double ms = (ctx->argumentCount < 3) ? msFromTime(t) : ctx->argument(2).toNumber(); - t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), min, sec, ms)))); - self->value.setDouble(t); - return self->value; -} - -Value DatePrototype::method_setUTCMinutes(SimpleCallContext *ctx) -{ - DateObject *self = ctx->thisObject.asDateObject(); - if (!self) - ctx->throwTypeError(); - - double t = self->value.asDouble(); - double min = ctx->argument(0).toNumber(); - double sec = (ctx->argumentCount < 2) ? SecFromTime(t) : ctx->argument(1).toNumber(); - double ms = (ctx->argumentCount < 3) ? msFromTime(t) : ctx->argument(2).toNumber(); - t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), min, sec, ms)))); - self->value.setDouble(t); - return self->value; -} - -Value DatePrototype::method_setHours(SimpleCallContext *ctx) -{ - DateObject *self = ctx->thisObject.asDateObject(); - if (!self) - ctx->throwTypeError(); - - double t = LocalTime(self->value.asDouble()); - double hour = ctx->argument(0).toNumber(); - double min = (ctx->argumentCount < 2) ? MinFromTime(t) : ctx->argument(1).toNumber(); - double sec = (ctx->argumentCount < 3) ? SecFromTime(t) : ctx->argument(2).toNumber(); - double ms = (ctx->argumentCount < 4) ? msFromTime(t) : ctx->argument(3).toNumber(); - t = TimeClip(UTC(MakeDate(Day(t), MakeTime(hour, min, sec, ms)))); - self->value.setDouble(t); - return self->value; -} - -Value DatePrototype::method_setUTCHours(SimpleCallContext *ctx) -{ - DateObject *self = ctx->thisObject.asDateObject(); - if (!self) - ctx->throwTypeError(); - - double t = self->value.asDouble(); - double hour = ctx->argument(0).toNumber(); - double min = (ctx->argumentCount < 2) ? MinFromTime(t) : ctx->argument(1).toNumber(); - double sec = (ctx->argumentCount < 3) ? SecFromTime(t) : ctx->argument(2).toNumber(); - double ms = (ctx->argumentCount < 4) ? msFromTime(t) : ctx->argument(3).toNumber(); - t = TimeClip(UTC(MakeDate(Day(t), MakeTime(hour, min, sec, ms)))); - self->value.setDouble(t); - return self->value; -} - -Value DatePrototype::method_setDate(SimpleCallContext *ctx) -{ - DateObject *self = ctx->thisObject.asDateObject(); - if (!self) - ctx->throwTypeError(); - - double t = LocalTime(self->value.asDouble()); - double date = ctx->argument(0).toNumber(); - t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t)))); - self->value.setDouble(t); - return self->value; -} - -Value DatePrototype::method_setUTCDate(SimpleCallContext *ctx) -{ - DateObject *self = ctx->thisObject.asDateObject(); - if (!self) - ctx->throwTypeError(); - - double t = self->value.asDouble(); - double date = ctx->argument(0).toNumber(); - t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t)))); - self->value.setDouble(t); - return self->value; -} - -Value DatePrototype::method_setMonth(SimpleCallContext *ctx) -{ - DateObject *self = ctx->thisObject.asDateObject(); - if (!self) - ctx->throwTypeError(); - - double t = LocalTime(self->value.asDouble()); - double month = ctx->argument(0).toNumber(); - double date = (ctx->argumentCount < 2) ? DateFromTime(t) : ctx->argument(1).toNumber(); - t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t)))); - self->value.setDouble(t); - return self->value; -} - -Value DatePrototype::method_setUTCMonth(SimpleCallContext *ctx) -{ - DateObject *self = ctx->thisObject.asDateObject(); - if (!self) - ctx->throwTypeError(); - - double t = self->value.asDouble(); - double month = ctx->argument(0).toNumber(); - double date = (ctx->argumentCount < 2) ? DateFromTime(t) : ctx->argument(1).toNumber(); - t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t)))); - self->value.setDouble(t); - return self->value; -} - -Value DatePrototype::method_setYear(SimpleCallContext *ctx) -{ - DateObject *self = ctx->thisObject.asDateObject(); - if (!self) - ctx->throwTypeError(); - - double t = self->value.asDouble(); - if (std::isnan(t)) - t = 0; - else - t = LocalTime(t); - double year = ctx->argument(0).toNumber(); - double r; - if (std::isnan(year)) { - r = qSNaN(); - } else { - if ((Value::toInteger(year) >= 0) && (Value::toInteger(year) <= 99)) - year += 1900; - r = MakeDay(year, MonthFromTime(t), DateFromTime(t)); - r = UTC(MakeDate(r, TimeWithinDay(t))); - r = TimeClip(r); - } - self->value.setDouble(r); - return self->value; -} - -Value DatePrototype::method_setUTCFullYear(SimpleCallContext *ctx) -{ - DateObject *self = ctx->thisObject.asDateObject(); - if (!self) - ctx->throwTypeError(); - - double t = self->value.asDouble(); - double year = ctx->argument(0).toNumber(); - double month = (ctx->argumentCount < 2) ? MonthFromTime(t) : ctx->argument(1).toNumber(); - double date = (ctx->argumentCount < 3) ? DateFromTime(t) : ctx->argument(2).toNumber(); - t = TimeClip(UTC(MakeDate(MakeDay(year, month, date), TimeWithinDay(t)))); - self->value.setDouble(t); - return self->value; -} - -Value DatePrototype::method_setFullYear(SimpleCallContext *ctx) -{ - DateObject *self = ctx->thisObject.asDateObject(); - if (!self) - ctx->throwTypeError(); - - double t = LocalTime(self->value.asDouble()); - if (std::isnan(t)) - t = 0; - double year = ctx->argument(0).toNumber(); - double month = (ctx->argumentCount < 2) ? MonthFromTime(t) : ctx->argument(1).toNumber(); - double date = (ctx->argumentCount < 3) ? DateFromTime(t) : ctx->argument(2).toNumber(); - t = TimeClip(UTC(MakeDate(MakeDay(year, month, date), TimeWithinDay(t)))); - self->value.setDouble(t); - return self->value; -} - -Value DatePrototype::method_toUTCString(SimpleCallContext *ctx) -{ - DateObject *self = ctx->thisObject.asDateObject(); - if (!self) - ctx->throwTypeError(); - - double t = self->value.asDouble(); - return Value::fromString(ctx, ToUTCString(t)); -} - -static void addZeroPrefixedInt(QString &str, int num, int nDigits) -{ - str.resize(str.size() + nDigits); - - QChar *c = str.data() + str.size() - 1; - while (nDigits) { - *c = QChar(num % 10 + '0'); - num /= 10; - --c; - --nDigits; - } -} - -Value DatePrototype::method_toISOString(SimpleCallContext *ctx) -{ - DateObject *self = ctx->thisObject.asDateObject(); - if (!self) - ctx->throwTypeError(); - - double t = self->value.asDouble(); - if (!std::isfinite(t)) - ctx->throwRangeError(ctx->thisObject); - - QString result; - int year = (int)YearFromTime(t); - if (year < 0 || year > 9999) { - if (qAbs(year) >= 1000000) - return Value::fromString(ctx, QStringLiteral("Invalid Date")); - result += year < 0 ? '-' : '+'; - year = qAbs(year); - addZeroPrefixedInt(result, year, 6); - } else { - addZeroPrefixedInt(result, year, 4); - } - result += '-'; - addZeroPrefixedInt(result, (int)MonthFromTime(t) + 1, 2); - result += '-'; - addZeroPrefixedInt(result, (int)DateFromTime(t), 2); - result += 'T'; - addZeroPrefixedInt(result, HourFromTime(t), 2); - result += ':'; - addZeroPrefixedInt(result, MinFromTime(t), 2); - result += ':'; - addZeroPrefixedInt(result, SecFromTime(t), 2); - result += '.'; - addZeroPrefixedInt(result, msFromTime(t), 3); - result += 'Z'; - - return Value::fromString(ctx, result); -} - -Value DatePrototype::method_toJSON(SimpleCallContext *ctx) -{ - Value O = __qmljs_to_object(ctx, ctx->thisObject); - Value tv = __qmljs_to_primitive(O, NUMBER_HINT); - - if (tv.isNumber() && !std::isfinite(tv.toNumber())) - return Value::nullValue(); - - FunctionObject *toIso = O.objectValue()->get(ctx->engine->newString(QStringLiteral("toISOString"))).asFunctionObject(); - - if (!toIso) - ctx->throwTypeError(); - - return toIso->call(ctx->thisObject, 0, 0); -} - -void DatePrototype::timezoneUpdated() -{ - LocalTZA = getLocalTZA(); -} diff --git a/src/qml/qml/v4/qv4dateobject_p.h b/src/qml/qml/v4/qv4dateobject_p.h deleted file mode 100644 index 4e833e143f..0000000000 --- a/src/qml/qml/v4/qv4dateobject_p.h +++ /dev/null @@ -1,137 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QV4DATEOBJECT_P_H -#define QV4DATEOBJECT_P_H - -#include "qv4object_p.h" -#include "qv4functionobject_p.h" -#include - -QT_BEGIN_NAMESPACE - -class QDateTime; - -namespace QV4 { - -struct DateObject: Object { - Value value; - DateObject(ExecutionEngine *engine, const Value &value): Object(engine), value(value) { type = Type_DateObject; } - DateObject(ExecutionEngine *engine, const QDateTime &value); - - QDateTime toQDateTime() const; -}; - -struct DateCtor: FunctionObject -{ - DateCtor(ExecutionContext *scope); - - static Value construct(Managed *, Value *args, int argc); - static Value call(Managed *that, const Value &, Value *, int); - -protected: - static const ManagedVTable static_vtbl; -}; - -struct DatePrototype: DateObject -{ - DatePrototype(ExecutionEngine *engine): DateObject(engine, Value::fromDouble(qSNaN())) {} - void init(ExecutionContext *ctx, const Value &ctor); - - static double getThisDate(ExecutionContext *ctx); - - static Value method_parse(SimpleCallContext *ctx); - static Value method_UTC(SimpleCallContext *ctx); - static Value method_now(SimpleCallContext *ctx); - - static Value method_toString(SimpleCallContext *ctx); - static Value method_toDateString(SimpleCallContext *ctx); - static Value method_toTimeString(SimpleCallContext *ctx); - static Value method_toLocaleString(SimpleCallContext *ctx); - static Value method_toLocaleDateString(SimpleCallContext *ctx); - static Value method_toLocaleTimeString(SimpleCallContext *ctx); - static Value method_valueOf(SimpleCallContext *ctx); - static Value method_getTime(SimpleCallContext *ctx); - static Value method_getYear(SimpleCallContext *ctx); - static Value method_getFullYear(SimpleCallContext *ctx); - static Value method_getUTCFullYear(SimpleCallContext *ctx); - static Value method_getMonth(SimpleCallContext *ctx); - static Value method_getUTCMonth(SimpleCallContext *ctx); - static Value method_getDate(SimpleCallContext *ctx); - static Value method_getUTCDate(SimpleCallContext *ctx); - static Value method_getDay(SimpleCallContext *ctx); - static Value method_getUTCDay(SimpleCallContext *ctx); - static Value method_getHours(SimpleCallContext *ctx); - static Value method_getUTCHours(SimpleCallContext *ctx); - static Value method_getMinutes(SimpleCallContext *ctx); - static Value method_getUTCMinutes(SimpleCallContext *ctx); - static Value method_getSeconds(SimpleCallContext *ctx); - static Value method_getUTCSeconds(SimpleCallContext *ctx); - static Value method_getMilliseconds(SimpleCallContext *ctx); - static Value method_getUTCMilliseconds(SimpleCallContext *ctx); - static Value method_getTimezoneOffset(SimpleCallContext *ctx); - static Value method_setTime(SimpleCallContext *ctx); - static Value method_setMilliseconds(SimpleCallContext *ctx); - static Value method_setUTCMilliseconds(SimpleCallContext *ctx); - static Value method_setSeconds(SimpleCallContext *ctx); - static Value method_setUTCSeconds(SimpleCallContext *ctx); - static Value method_setMinutes(SimpleCallContext *ctx); - static Value method_setUTCMinutes(SimpleCallContext *ctx); - static Value method_setHours(SimpleCallContext *ctx); - static Value method_setUTCHours(SimpleCallContext *ctx); - static Value method_setDate(SimpleCallContext *ctx); - static Value method_setUTCDate(SimpleCallContext *ctx); - static Value method_setMonth(SimpleCallContext *ctx); - static Value method_setUTCMonth(SimpleCallContext *ctx); - static Value method_setYear(SimpleCallContext *ctx); - static Value method_setFullYear(SimpleCallContext *ctx); - static Value method_setUTCFullYear(SimpleCallContext *ctx); - static Value method_toUTCString(SimpleCallContext *ctx); - static Value method_toISOString(SimpleCallContext *ctx); - static Value method_toJSON(SimpleCallContext *ctx); - - static void timezoneUpdated(); -}; - -} - -QT_END_NAMESPACE - -#endif // QV4ECMAOBJECTS_P_H diff --git a/src/qml/qml/v4/qv4debugging.cpp b/src/qml/qml/v4/qv4debugging.cpp deleted file mode 100644 index 624390d8f2..0000000000 --- a/src/qml/qml/v4/qv4debugging.cpp +++ /dev/null @@ -1,372 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4debugging_p.h" -#include "qv4object_p.h" -#include "qv4functionobject_p.h" -#include "qv4function_p.h" -#include "moth/qv4instr_moth_p.h" -#include - -using namespace QV4; -using namespace QV4::Debugging; - -Debugger::Debugger(QV4::ExecutionEngine *engine) - : _engine(engine) - , m_agent(0) - , m_state(Running) - , m_pauseRequested(false) - , m_currentInstructionPointer(0) -{ - qMetaTypeId(); -} - -Debugger::~Debugger() -{ - detachFromAgent(); -} - -void Debugger::attachToAgent(DebuggerAgent *agent) -{ - Q_ASSERT(!m_agent); - m_agent = agent; -} - -void Debugger::detachFromAgent() -{ - DebuggerAgent *agent = 0; - { - QMutexLocker locker(&m_lock); - agent = m_agent; - m_agent = 0; - } - if (agent) - agent->removeDebugger(this); -} - -void Debugger::pause() -{ - QMutexLocker locker(&m_lock); - if (m_state == Paused) - return; - m_pauseRequested = true; -} - -void Debugger::resume() -{ - QMutexLocker locker(&m_lock); - Q_ASSERT(m_state == Paused); - m_runningCondition.wakeAll(); -} - -void Debugger::addBreakPoint(const QString &fileName, int lineNumber) -{ - QMutexLocker locker(&m_lock); - if (!m_pendingBreakPointsToRemove.remove(fileName, lineNumber)) - m_pendingBreakPointsToAdd.add(fileName, lineNumber); - m_havePendingBreakPoints = !m_pendingBreakPointsToAdd.isEmpty() || !m_pendingBreakPointsToRemove.isEmpty(); -} - -void Debugger::removeBreakPoint(const QString &fileName, int lineNumber) -{ - QMutexLocker locker(&m_lock); - if (!m_pendingBreakPointsToAdd.remove(fileName, lineNumber)) - m_pendingBreakPointsToRemove.add(fileName, lineNumber); - m_havePendingBreakPoints = !m_pendingBreakPointsToAdd.isEmpty() || !m_pendingBreakPointsToRemove.isEmpty(); -} - -Debugger::ExecutionState Debugger::currentExecutionState(const uchar *code) const -{ - if (!code) - code = m_currentInstructionPointer; - // ### Locking - ExecutionState state; - - QV4::ExecutionContext *context = _engine->current; - QV4::Function *function = 0; - if (CallContext *callCtx = context->asCallContext()) - function = callCtx->function->function; - else { - Q_ASSERT(context->type == QV4::ExecutionContext::Type_GlobalContext); - function = context->engine->globalCode; - } - - state.function = function; - state.fileName = function->sourceFile; - - qptrdiff relativeProgramCounter = code - function->codeData; - state.lineNumber = function->lineNumberForProgramCounter(relativeProgramCounter); - - return state; -} - -void Debugger::setPendingBreakpoints(Function *function) -{ - m_pendingBreakPointsToAddToFutureCode.applyToFunction(function, /*removeBreakPoints*/ false); -} - -void Debugger::maybeBreakAtInstruction(const uchar *code, bool breakPointHit) -{ - QMutexLocker locker(&m_lock); - m_currentInstructionPointer = code; - - // Do debugger internal work - if (m_havePendingBreakPoints) { - - if (breakPointHit) { - ExecutionState state = currentExecutionState(); - breakPointHit = !m_pendingBreakPointsToRemove.contains(state.fileName, state.lineNumber); - } - - applyPendingBreakPoints(); - } - - // Serve debugging requests from the agent - if (m_pauseRequested) { - m_pauseRequested = false; - pauseAndWait(); - } else if (breakPointHit) - pauseAndWait(); - - if (!m_pendingBreakPointsToAdd.isEmpty() || !m_pendingBreakPointsToRemove.isEmpty()) - applyPendingBreakPoints(); -} - -void Debugger::aboutToThrow(const QV4::Value &value) -{ - qDebug() << "*** We are about to throw..."; -} - -void Debugger::pauseAndWait() -{ - m_state = Paused; - QMetaObject::invokeMethod(m_agent, "debuggerPaused", Qt::QueuedConnection, Q_ARG(QV4::Debugging::Debugger*, this)); - m_runningCondition.wait(&m_lock); - m_state = Running; -} - -void Debugger::applyPendingBreakPoints() -{ - foreach (Function *function, _engine->functions) { - m_pendingBreakPointsToAdd.applyToFunction(function, /*removeBreakPoints*/false); - m_pendingBreakPointsToRemove.applyToFunction(function, /*removeBreakPoints*/true); - } - - for (BreakPoints::ConstIterator it = m_pendingBreakPointsToAdd.constBegin(), - end = m_pendingBreakPointsToAdd.constEnd(); it != end; ++it) { - foreach (int lineNumber, it.value()) - m_pendingBreakPointsToAddToFutureCode.add(it.key(), lineNumber); - } - - m_pendingBreakPointsToAdd.clear(); - m_pendingBreakPointsToRemove.clear(); - m_havePendingBreakPoints = false; -} - -static void realDumpValue(QV4::Value v, QV4::ExecutionContext *ctx, std::string prefix) -{ - using namespace QV4; - using namespace std; - cout << prefix << "tag: " << hex << v.tag << dec << endl << prefix << "\t-> "; - switch (v.type()) { - case Value::Undefined_Type: cout << "Undefined" << endl; return; - case Value::Null_Type: cout << "Null" << endl; return; - case Value::Boolean_Type: cout << "Boolean"; break; - case Value::Integer_Type: cout << "Integer"; break; - case Value::Object_Type: cout << "Object"; break; - case Value::String_Type: cout << "String"; break; - default: cout << "UNKNOWN" << endl; return; - } - cout << endl; - - if (v.isBoolean()) { - cout << prefix << "\t-> " << (v.booleanValue() ? "TRUE" : "FALSE") << endl; - return; - } - - if (v.isInteger()) { - cout << prefix << "\t-> " << v.integerValue() << endl; - return; - } - - if (v.isDouble()) { - cout << prefix << "\t-> " << v.doubleValue() << endl; - return; - } - - if (v.isString()) { - // maybe check something on the Managed object? - cout << prefix << "\t-> @" << hex << v.stringValue() << endl; - cout << prefix << "\t-> \"" << qPrintable(v.stringValue()->toQString()) << "\"" << endl; - return; - } - - Object *o = v.objectValue(); - if (!o) - return; - - cout << prefix << "\t-> @" << hex << o << endl; - cout << prefix << "object type: " << o->internalType() << endl << prefix << "\t-> "; - switch (o->internalType()) { - case QV4::Managed::Type_Invalid: cout << "Invalid"; break; - case QV4::Managed::Type_String: cout << "String"; break; - case QV4::Managed::Type_Object: cout << "Object"; break; - case QV4::Managed::Type_ArrayObject: cout << "ArrayObject"; break; - case QV4::Managed::Type_FunctionObject: cout << "FunctionObject"; break; - case QV4::Managed::Type_BooleanObject: cout << "BooleanObject"; break; - case QV4::Managed::Type_NumberObject: cout << "NumberObject"; break; - case QV4::Managed::Type_StringObject: cout << "StringObject"; break; - case QV4::Managed::Type_DateObject: cout << "DateObject"; break; - case QV4::Managed::Type_RegExpObject: cout << "RegExpObject"; break; - case QV4::Managed::Type_ErrorObject: cout << "ErrorObject"; break; - case QV4::Managed::Type_ArgumentsObject: cout << "ArgumentsObject"; break; - case QV4::Managed::Type_JSONObject: cout << "JSONObject"; break; - case QV4::Managed::Type_MathObject: cout << "MathObject"; break; - case QV4::Managed::Type_ForeachIteratorObject: cout << "ForeachIteratorObject"; break; - default: cout << "UNKNOWN" << endl; return; - } - cout << endl; - - cout << prefix << "properties:" << endl; - ForEachIteratorObject it(ctx, o); - for (Value name = it.nextPropertyName(); !name.isNull(); name = it.nextPropertyName()) { - cout << prefix << "\t\"" << qPrintable(name.stringValue()->toQString()) << "\"" << endl; - PropertyAttributes attrs; - Property *d = o->__getOwnProperty__(name.stringValue(), &attrs); - Value pval = o->getValue(d, attrs); - cout << prefix << "\tvalue:" << endl; - realDumpValue(pval, ctx, prefix + "\t"); - } -} - -void dumpValue(QV4::Value v, QV4::ExecutionContext *ctx) -{ - realDumpValue(v, ctx, std::string("")); -} - - -void DebuggerAgent::addDebugger(Debugger *debugger) -{ - Q_ASSERT(!m_debuggers.contains(debugger)); - m_debuggers << debugger; - debugger->attachToAgent(this); -} - -void DebuggerAgent::removeDebugger(Debugger *debugger) -{ - m_debuggers.removeAll(debugger); - debugger->detachFromAgent(); -} - -void DebuggerAgent::pause(Debugger *debugger) -{ - debugger->pause(); -} - -void DebuggerAgent::addBreakPoint(Debugger *debugger, const QString &fileName, int lineNumber) -{ - debugger->addBreakPoint(fileName, lineNumber); -} - -void DebuggerAgent::removeBreakPoint(Debugger *debugger, const QString &fileName, int lineNumber) -{ - debugger->removeBreakPoint(fileName, lineNumber); -} - -DebuggerAgent::~DebuggerAgent() -{ - Q_ASSERT(m_debuggers.isEmpty()); -} - -void Debugger::BreakPoints::add(const QString &fileName, int lineNumber) -{ - QList &lines = (*this)[fileName]; - if (!lines.contains(lineNumber)) { - lines.append(lineNumber); - qSort(lines); - } -} - -bool Debugger::BreakPoints::remove(const QString &fileName, int lineNumber) -{ - Iterator breakPoints = find(fileName); - if (breakPoints == constEnd()) - return false; - return breakPoints->removeAll(lineNumber) > 0; -} - -bool Debugger::BreakPoints::contains(const QString &fileName, int lineNumber) const -{ - ConstIterator breakPoints = find(fileName); - if (breakPoints == constEnd()) - return false; - return breakPoints->contains(lineNumber); -} - -void Debugger::BreakPoints::applyToFunction(Function *function, bool removeBreakPoints) -{ - Iterator breakPointsForFile = find(function->sourceFile); - if (breakPointsForFile == end()) - return; - - QList::Iterator breakPoint = breakPointsForFile->begin(); - while (breakPoint != breakPointsForFile->end()) { - bool breakPointFound = false; - for (QVector::ConstIterator mapping = function->lineNumberMappings.constBegin(), - end = function->lineNumberMappings.constEnd(); mapping != end; ++mapping) { - if (mapping->lineNumber == *breakPoint) { - uchar *codePtr = const_cast(function->codeData) + mapping->codeOffset; - QQmlJS::Moth::Instr *instruction = reinterpret_cast(codePtr); - instruction->common.breakPoint = !removeBreakPoints; - // Continue setting the next break point. - breakPointFound = true; - break; - } - } - if (breakPointFound) - breakPoint = breakPointsForFile->erase(breakPoint); - else - ++breakPoint; - } - - if (breakPointsForFile->isEmpty()) - erase(breakPointsForFile); -} diff --git a/src/qml/qml/v4/qv4debugging_p.h b/src/qml/qml/v4/qv4debugging_p.h deleted file mode 100644 index 4a273be732..0000000000 --- a/src/qml/qml/v4/qv4debugging_p.h +++ /dev/null @@ -1,161 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef DEBUGGING_H -#define DEBUGGING_H - -#include "qv4global_p.h" -#include "qv4engine_p.h" -#include "qv4context_p.h" -#include "qv4jsir_p.h" - -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE - -namespace QV4 { - -struct Function; - -namespace Debugging { - -class DebuggerAgent; - -class Q_QML_EXPORT Debugger -{ -public: - enum State { - Running, - Paused - }; - - Debugger(ExecutionEngine *_engine); - ~Debugger(); - - void attachToAgent(DebuggerAgent *agent); - void detachFromAgent(); - - void pause(); - void resume(); - - State state() const { return m_state; } - - void addBreakPoint(const QString &fileName, int lineNumber); - void removeBreakPoint(const QString &fileName, int lineNumber); - - struct ExecutionState - { - ExecutionState() : lineNumber(-1), function(0) {} - QString fileName; - int lineNumber; - Function *function; - }; - - ExecutionState currentExecutionState(const uchar *code = 0) const; - - bool pauseAtNextOpportunity() const { - return m_pauseRequested || m_havePendingBreakPoints; - } - void setPendingBreakpoints(Function *function); - -public: // compile-time interface - void maybeBreakAtInstruction(const uchar *code, bool breakPointHit); - -public: // execution hooks - void aboutToThrow(const Value &value); - -private: - // requires lock to be held - void pauseAndWait(); - - void applyPendingBreakPoints(); - - struct BreakPoints : public QHash > - { - void add(const QString &fileName, int lineNumber); - bool remove(const QString &fileName, int lineNumber); - bool contains(const QString &fileName, int lineNumber) const; - void applyToFunction(Function *function, bool removeBreakPoints); - }; - - QV4::ExecutionEngine *_engine; - DebuggerAgent *m_agent; - QMutex m_lock; - QWaitCondition m_runningCondition; - State m_state; - bool m_pauseRequested; - bool m_havePendingBreakPoints; - BreakPoints m_pendingBreakPointsToAdd; - BreakPoints m_pendingBreakPointsToAddToFutureCode; - BreakPoints m_pendingBreakPointsToRemove; - const uchar *m_currentInstructionPointer; -}; - -class Q_QML_EXPORT DebuggerAgent : public QObject -{ - Q_OBJECT -public: - ~DebuggerAgent(); - - void addDebugger(Debugger *debugger); - void removeDebugger(Debugger *debugger); - - void pause(Debugger *debugger); - void addBreakPoint(Debugger *debugger, const QString &fileName, int lineNumber); - void removeBreakPoint(Debugger *debugger, const QString &fileName, int lineNumber); - - Q_INVOKABLE virtual void debuggerPaused(QV4::Debugging::Debugger *debugger) = 0; - -protected: - QList m_debuggers; -}; - -} // namespace Debugging -} // namespace QV4 - -QT_END_NAMESPACE - -Q_DECLARE_METATYPE(QV4::Debugging::Debugger*) - -#endif // DEBUGGING_H diff --git a/src/qml/qml/v4/qv4engine.cpp b/src/qml/qml/v4/qv4engine.cpp deleted file mode 100644 index 0b7c850aa6..0000000000 --- a/src/qml/qml/v4/qv4engine.cpp +++ /dev/null @@ -1,837 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "qv4function_p.h" -#include -#include -#include -#include -#include -#include "qv4mm_p.h" -#include -#include -#include -#include -#include -#include -#include "qv4debugging_p.h" -#include "qv4executableallocator_p.h" -#include "qv4sequenceobject_p.h" -#include "qv4qobjectwrapper_p.h" -#include "qv4qmlextensions_p.h" -#include "qv4stacktrace_p.h" - -#ifdef V4_ENABLE_JIT -#include "qv4isel_masm_p.h" -#endif // V4_ENABLE_JIT - -#include "qv4isel_moth_p.h" - -QT_BEGIN_NAMESPACE - -using namespace QV4; - -static QBasicAtomicInt engineSerial = Q_BASIC_ATOMIC_INITIALIZER(1); - -ExecutionEngine::ExecutionEngine(QQmlJS::EvalISelFactory *factory) - : memoryManager(new QV4::MemoryManager) - , executableAllocator(new QV4::ExecutableAllocator) - , regExpAllocator(new QV4::ExecutableAllocator) - , bumperPointerAllocator(new WTF::BumpPointerAllocator) - , debugger(0) - , globalObject(0) - , globalCode(0) - , functionsNeedSort(false) - , m_engineId(engineSerial.fetchAndAddOrdered(1)) - , regExpCache(0) - , m_multiplyWrappedQObjects(0) - , m_qmlExtensions(0) -{ - MemoryManager::GCBlocker gcBlocker(memoryManager); - - if (!factory) { -#ifdef V4_ENABLE_JIT - factory = new QQmlJS::MASM::ISelFactory; -#else // !V4_ENABLE_JIT - factory = new QQmlJS::Moth::ISelFactory; -#endif // V4_ENABLE_JIT - } - iselFactory.reset(factory); - - memoryManager->setExecutionEngine(this); - - identifierTable = new IdentifierTable(this); - - emptyClass = new (classPool.allocate(sizeof(InternalClass))) InternalClass(this); - - id_undefined = newIdentifier(QStringLiteral("undefined")); - id_null = newIdentifier(QStringLiteral("null")); - id_true = newIdentifier(QStringLiteral("true")); - id_false = newIdentifier(QStringLiteral("false")); - id_boolean = newIdentifier(QStringLiteral("boolean")); - id_number = newIdentifier(QStringLiteral("number")); - id_string = newIdentifier(QStringLiteral("string")); - id_object = newIdentifier(QStringLiteral("object")); - id_function = newIdentifier(QStringLiteral("function")); - id_length = newIdentifier(QStringLiteral("length")); - id_prototype = newIdentifier(QStringLiteral("prototype")); - id_constructor = newIdentifier(QStringLiteral("constructor")); - id_arguments = newIdentifier(QStringLiteral("arguments")); - id_caller = newIdentifier(QStringLiteral("caller")); - id_this = newIdentifier(QStringLiteral("this")); - id___proto__ = newIdentifier(QStringLiteral("__proto__")); - id_enumerable = newIdentifier(QStringLiteral("enumerable")); - id_configurable = newIdentifier(QStringLiteral("configurable")); - id_writable = newIdentifier(QStringLiteral("writable")); - id_value = newIdentifier(QStringLiteral("value")); - id_get = newIdentifier(QStringLiteral("get")); - id_set = newIdentifier(QStringLiteral("set")); - id_eval = newIdentifier(QStringLiteral("eval")); - id_uintMax = newIdentifier(QStringLiteral("4294967295")); - id_name = newIdentifier(QStringLiteral("name")); - - arrayClass = emptyClass->addMember(id_length, Attr_NotConfigurable|Attr_NotEnumerable); - initRootContext(); - - objectPrototype = new (memoryManager) ObjectPrototype(this); - stringPrototype = new (memoryManager) StringPrototype(this); - numberPrototype = new (memoryManager) NumberPrototype(this); - booleanPrototype = new (memoryManager) BooleanPrototype(this); - arrayPrototype = new (memoryManager) ArrayPrototype(rootContext); - datePrototype = new (memoryManager) DatePrototype(this); - functionPrototype = new (memoryManager) FunctionPrototype(rootContext); - regExpPrototype = new (memoryManager) RegExpPrototype(this); - errorPrototype = new (memoryManager) ErrorPrototype(this); - evalErrorPrototype = new (memoryManager) EvalErrorPrototype(this); - rangeErrorPrototype = new (memoryManager) RangeErrorPrototype(this); - referenceErrorPrototype = new (memoryManager) ReferenceErrorPrototype(this); - syntaxErrorPrototype = new (memoryManager) SyntaxErrorPrototype(this); - typeErrorPrototype = new (memoryManager) TypeErrorPrototype(this); - uRIErrorPrototype = new (memoryManager) URIErrorPrototype(this); - - variantPrototype = new (memoryManager) VariantPrototype(this); - sequencePrototype = new (memoryManager) SequencePrototype(this); - - stringPrototype->prototype = objectPrototype; - numberPrototype->prototype = objectPrototype; - booleanPrototype->prototype = objectPrototype; - arrayPrototype->prototype = objectPrototype; - datePrototype->prototype = objectPrototype; - functionPrototype->prototype = objectPrototype; - regExpPrototype->prototype = objectPrototype; - errorPrototype->prototype = objectPrototype; - evalErrorPrototype->prototype = objectPrototype; - rangeErrorPrototype->prototype = objectPrototype; - referenceErrorPrototype->prototype = objectPrototype; - syntaxErrorPrototype->prototype = objectPrototype; - typeErrorPrototype->prototype = objectPrototype; - uRIErrorPrototype->prototype = objectPrototype; - - objectCtor = Value::fromObject(new (memoryManager) ObjectCtor(rootContext)); - stringCtor = Value::fromObject(new (memoryManager) StringCtor(rootContext)); - numberCtor = Value::fromObject(new (memoryManager) NumberCtor(rootContext)); - booleanCtor = Value::fromObject(new (memoryManager) BooleanCtor(rootContext)); - arrayCtor = Value::fromObject(new (memoryManager) ArrayCtor(rootContext)); - functionCtor = Value::fromObject(new (memoryManager) FunctionCtor(rootContext)); - dateCtor = Value::fromObject(new (memoryManager) DateCtor(rootContext)); - regExpCtor = Value::fromObject(new (memoryManager) RegExpCtor(rootContext)); - errorCtor = Value::fromObject(new (memoryManager) ErrorCtor(rootContext)); - evalErrorCtor = Value::fromObject(new (memoryManager) EvalErrorCtor(rootContext)); - rangeErrorCtor = Value::fromObject(new (memoryManager) RangeErrorCtor(rootContext)); - referenceErrorCtor = Value::fromObject(new (memoryManager) ReferenceErrorCtor(rootContext)); - syntaxErrorCtor = Value::fromObject(new (memoryManager) SyntaxErrorCtor(rootContext)); - typeErrorCtor = Value::fromObject(new (memoryManager) TypeErrorCtor(rootContext)); - uRIErrorCtor = Value::fromObject(new (memoryManager) URIErrorCtor(rootContext)); - - objectCtor.objectValue()->prototype = functionPrototype; - stringCtor.objectValue()->prototype = functionPrototype; - numberCtor.objectValue()->prototype = functionPrototype; - booleanCtor.objectValue()->prototype = functionPrototype; - arrayCtor.objectValue()->prototype = functionPrototype; - functionCtor.objectValue()->prototype = functionPrototype; - dateCtor.objectValue()->prototype = functionPrototype; - regExpCtor.objectValue()->prototype = functionPrototype; - errorCtor.objectValue()->prototype = functionPrototype; - evalErrorCtor.objectValue()->prototype = functionPrototype; - rangeErrorCtor.objectValue()->prototype = functionPrototype; - referenceErrorCtor.objectValue()->prototype = functionPrototype; - syntaxErrorCtor.objectValue()->prototype = functionPrototype; - typeErrorCtor.objectValue()->prototype = functionPrototype; - uRIErrorCtor.objectValue()->prototype = functionPrototype; - - objectPrototype->init(rootContext, objectCtor); - stringPrototype->init(this, stringCtor); - numberPrototype->init(rootContext, numberCtor); - booleanPrototype->init(rootContext, booleanCtor); - arrayPrototype->init(rootContext, arrayCtor); - datePrototype->init(rootContext, dateCtor); - functionPrototype->init(rootContext, functionCtor); - regExpPrototype->init(rootContext, regExpCtor); - errorPrototype->init(this, errorCtor); - evalErrorPrototype->init(this, evalErrorCtor); - rangeErrorPrototype->init(this, rangeErrorCtor); - referenceErrorPrototype->init(this, referenceErrorCtor); - syntaxErrorPrototype->init(this, syntaxErrorCtor); - typeErrorPrototype->init(this, typeErrorCtor); - uRIErrorPrototype->init(this, uRIErrorCtor); - - variantPrototype->init(this); - sequencePrototype->init(this); - - // - // set up the global object - // - globalObject = newObject(/*rootContext*/); - rootContext->global = globalObject; - rootContext->thisObject = Value::fromObject(globalObject); - - globalObject->defineDefaultProperty(rootContext, QStringLiteral("Object"), objectCtor); - globalObject->defineDefaultProperty(rootContext, QStringLiteral("String"), stringCtor); - globalObject->defineDefaultProperty(rootContext, QStringLiteral("Number"), numberCtor); - globalObject->defineDefaultProperty(rootContext, QStringLiteral("Boolean"), booleanCtor); - globalObject->defineDefaultProperty(rootContext, QStringLiteral("Array"), arrayCtor); - globalObject->defineDefaultProperty(rootContext, QStringLiteral("Function"), functionCtor); - globalObject->defineDefaultProperty(rootContext, QStringLiteral("Date"), dateCtor); - globalObject->defineDefaultProperty(rootContext, QStringLiteral("RegExp"), regExpCtor); - globalObject->defineDefaultProperty(rootContext, QStringLiteral("Error"), errorCtor); - globalObject->defineDefaultProperty(rootContext, QStringLiteral("EvalError"), evalErrorCtor); - globalObject->defineDefaultProperty(rootContext, QStringLiteral("RangeError"), rangeErrorCtor); - globalObject->defineDefaultProperty(rootContext, QStringLiteral("ReferenceError"), referenceErrorCtor); - globalObject->defineDefaultProperty(rootContext, QStringLiteral("SyntaxError"), syntaxErrorCtor); - globalObject->defineDefaultProperty(rootContext, QStringLiteral("TypeError"), typeErrorCtor); - globalObject->defineDefaultProperty(rootContext, QStringLiteral("URIError"), uRIErrorCtor); - globalObject->defineDefaultProperty(rootContext, QStringLiteral("Math"), Value::fromObject(new (memoryManager) MathObject(rootContext))); - globalObject->defineDefaultProperty(rootContext, QStringLiteral("JSON"), Value::fromObject(new (memoryManager) JsonObject(rootContext))); - - globalObject->defineReadonlyProperty(this, QStringLiteral("undefined"), Value::undefinedValue()); - globalObject->defineReadonlyProperty(this, QStringLiteral("NaN"), Value::fromDouble(std::numeric_limits::quiet_NaN())); - globalObject->defineReadonlyProperty(this, QStringLiteral("Infinity"), Value::fromDouble(Q_INFINITY)); - - evalFunction = new (memoryManager) EvalFunction(rootContext); - globalObject->defineDefaultProperty(rootContext, QStringLiteral("eval"), Value::fromObject(evalFunction)); - - globalObject->defineDefaultProperty(rootContext, QStringLiteral("parseInt"), GlobalFunctions::method_parseInt, 2); - globalObject->defineDefaultProperty(rootContext, QStringLiteral("parseFloat"), GlobalFunctions::method_parseFloat, 1); - globalObject->defineDefaultProperty(rootContext, QStringLiteral("isNaN"), GlobalFunctions::method_isNaN, 1); - globalObject->defineDefaultProperty(rootContext, QStringLiteral("isFinite"), GlobalFunctions::method_isFinite, 1); - globalObject->defineDefaultProperty(rootContext, QStringLiteral("decodeURI"), GlobalFunctions::method_decodeURI, 1); - globalObject->defineDefaultProperty(rootContext, QStringLiteral("decodeURIComponent"), GlobalFunctions::method_decodeURIComponent, 1); - globalObject->defineDefaultProperty(rootContext, QStringLiteral("encodeURI"), GlobalFunctions::method_encodeURI, 1); - globalObject->defineDefaultProperty(rootContext, QStringLiteral("encodeURIComponent"), GlobalFunctions::method_encodeURIComponent, 1); - globalObject->defineDefaultProperty(rootContext, QStringLiteral("escape"), GlobalFunctions::method_escape, 1); - globalObject->defineDefaultProperty(rootContext, QStringLiteral("unescape"), GlobalFunctions::method_unescape, 1); -} - -ExecutionEngine::~ExecutionEngine() -{ - delete debugger; - delete m_multiplyWrappedQObjects; - m_multiplyWrappedQObjects = 0; - delete memoryManager; - delete m_qmlExtensions; - emptyClass->destroy(); - delete identifierTable; - delete bumperPointerAllocator; - delete regExpCache; - UnwindHelper::deregisterFunctions(functions); - qDeleteAll(functions); - delete regExpAllocator; - delete executableAllocator; -} - -void ExecutionEngine::enableDebugger() -{ - Q_ASSERT(!debugger); - debugger = new Debugging::Debugger(this); - iselFactory.reset(new QQmlJS::Moth::ISelFactory); -} - -void ExecutionEngine::initRootContext() -{ - rootContext = static_cast(memoryManager->allocContext(sizeof(GlobalContext))); - current = rootContext; - current->parent = 0; - rootContext->initGlobalContext(this); -} - -InternalClass *ExecutionEngine::newClass(const InternalClass &other) -{ - return new (classPool.allocate(sizeof(InternalClass))) InternalClass(other); -} - -WithContext *ExecutionEngine::newWithContext(Object *with) -{ - WithContext *w = static_cast(memoryManager->allocContext(sizeof(WithContext))); - ExecutionContext *p = current; - current = w; - w->initWithContext(p, with); - return w; -} - -CatchContext *ExecutionEngine::newCatchContext(String *exceptionVarName, const Value &exceptionValue) -{ - CatchContext *c = static_cast(memoryManager->allocContext(sizeof(CatchContext))); - ExecutionContext *p = current; - current = c; - c->initCatchContext(p, exceptionVarName, exceptionValue); - return c; -} - -CallContext *ExecutionEngine::newCallContext(FunctionObject *f, const Value &thisObject, Value *args, int argc) -{ - CallContext *c = static_cast(memoryManager->allocContext(requiredMemoryForExecutionContect(f, argc))); - ExecutionContext *p = current; - current = c; - c->initCallContext(p, f, args, argc, thisObject); - - return c; -} - -CallContext *ExecutionEngine::newQmlContext(FunctionObject *f, Object *qml) -{ - CallContext *c = static_cast(memoryManager->allocContext(requiredMemoryForExecutionContect(f, 0))); - - ExecutionContext *p = current; - current = c; - c->initQmlContext(p, qml, f); - - return c; -} - -CallContext *ExecutionEngine::newCallContext(void *stackSpace, FunctionObject *f, const Value &thisObject, Value *args, int argc) -{ - CallContext *c; - uint memory = requiredMemoryForExecutionContect(f, argc); - if (f->needsActivation || memory > stackContextSize) { - c = static_cast(memoryManager->allocContext(memory)); - } else { - c = (CallContext *)stackSpace; -#ifndef QT_NO_DEBUG - c->next = (CallContext *)0x1; -#endif - } - - ExecutionContext *p = current; - current = c; - c->initCallContext(p, f, args, argc, thisObject); - - return c; -} - - -ExecutionContext *ExecutionEngine::pushGlobalContext() -{ - GlobalContext *g = static_cast(memoryManager->allocContext(sizeof(GlobalContext))); - ExecutionContext *oldNext = g->next; - *g = *rootContext; - g->next = oldNext; - g->parent = current; - current = g; - - return current; -} - -Function *ExecutionEngine::newFunction(const QString &name) -{ - Function *f = new Function(newIdentifier(name)); - functions.append(f); - functionsNeedSort = true; - return f; -} - -FunctionObject *ExecutionEngine::newBuiltinFunction(ExecutionContext *scope, String *name, Value (*code)(SimpleCallContext *)) -{ - BuiltinFunctionOld *f = new (memoryManager) BuiltinFunctionOld(scope, name, code); - return f; -} - -FunctionObject *ExecutionEngine::newScriptFunction(ExecutionContext *scope, Function *function) -{ - assert(function); - - ScriptFunction *f = new (memoryManager) ScriptFunction(scope, function); - return f; -} - -BoundFunction *ExecutionEngine::newBoundFunction(ExecutionContext *scope, FunctionObject *target, Value boundThis, const QVector &boundArgs) -{ - assert(target); - - BoundFunction *f = new (memoryManager) BoundFunction(scope, target, boundThis, boundArgs); - return f; -} - - -Object *ExecutionEngine::newObject() -{ - Object *object = new (memoryManager) Object(this); - object->prototype = objectPrototype; - return object; -} - -Object *ExecutionEngine::newObject(InternalClass *internalClass) -{ - Object *object = new (memoryManager) Object(this, internalClass); - object->prototype = objectPrototype; - return object; -} - -String *ExecutionEngine::newString(const QString &s) -{ - return new (memoryManager) String(this, s); -} - -String *ExecutionEngine::newIdentifier(const QString &text) -{ - return identifierTable->insertString(text); -} - -Object *ExecutionEngine::newStringObject(const Value &value) -{ - StringObject *object = new (memoryManager) StringObject(this, value); - object->prototype = stringPrototype; - return object; -} - -Object *ExecutionEngine::newNumberObject(const Value &value) -{ - NumberObject *object = new (memoryManager) NumberObject(this, value); - object->prototype = numberPrototype; - return object; -} - -Object *ExecutionEngine::newBooleanObject(const Value &value) -{ - Object *object = new (memoryManager) BooleanObject(this, value); - object->prototype = booleanPrototype; - return object; -} - -ArrayObject *ExecutionEngine::newArrayObject(int count) -{ - ArrayObject *object = new (memoryManager) ArrayObject(this); - object->prototype = arrayPrototype; - - if (count) { - if (count < 0x1000) - object->arrayReserve(count); - object->setArrayLengthUnchecked(count); - } - return object; -} - -ArrayObject *ExecutionEngine::newArrayObject(const QStringList &list) -{ - ArrayObject *object = new (memoryManager) ArrayObject(this, list); - object->prototype = arrayPrototype; - return object; -} - -DateObject *ExecutionEngine::newDateObject(const Value &value) -{ - DateObject *object = new (memoryManager) DateObject(this, value); - object->prototype = datePrototype; - return object; -} - -DateObject *ExecutionEngine::newDateObject(const QDateTime &dt) -{ - DateObject *object = new (memoryManager) DateObject(this, dt); - object->prototype = datePrototype; - return object; -} - -RegExpObject *ExecutionEngine::newRegExpObject(const QString &pattern, int flags) -{ - bool global = (flags & QQmlJS::V4IR::RegExp::RegExp_Global); - bool ignoreCase = false; - bool multiline = false; - if (flags & QQmlJS::V4IR::RegExp::RegExp_IgnoreCase) - ignoreCase = true; - if (flags & QQmlJS::V4IR::RegExp::RegExp_Multiline) - multiline = true; - - return newRegExpObject(RegExp::create(this, pattern, ignoreCase, multiline), global); -} - -RegExpObject *ExecutionEngine::newRegExpObject(RegExp* re, bool global) -{ - RegExpObject *object = new (memoryManager) RegExpObject(this, re, global); - object->prototype = regExpPrototype; - return object; -} - -RegExpObject *ExecutionEngine::newRegExpObject(const QRegExp &re) -{ - RegExpObject *object = new (memoryManager) RegExpObject(this, re); - object->prototype = regExpPrototype; - return object; -} - -Object *ExecutionEngine::newErrorObject(const Value &value) -{ - ErrorObject *object = new (memoryManager) ErrorObject(this, value); - object->prototype = errorPrototype; - return object; -} - -Object *ExecutionEngine::newSyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *message) -{ - return new (memoryManager) SyntaxErrorObject(ctx, message); -} - -Object *ExecutionEngine::newSyntaxErrorObject(const QString &message) -{ - return new (memoryManager) SyntaxErrorObject(this, message); -} - - -Object *ExecutionEngine::newReferenceErrorObject(const QString &message) -{ - return new (memoryManager) ReferenceErrorObject(this, message); -} - -Object *ExecutionEngine::newReferenceErrorObject(const QString &message, const QString &fileName, int lineNumber) -{ - return new (memoryManager) ReferenceErrorObject(this, message, fileName, lineNumber); -} - - -Object *ExecutionEngine::newTypeErrorObject(const QString &message) -{ - return new (memoryManager) TypeErrorObject(this, message); -} - -Object *ExecutionEngine::newRangeErrorObject(const QString &message) -{ - return new (memoryManager) RangeErrorObject(this, message); -} - -Object *ExecutionEngine::newURIErrorObject(Value message) -{ - return new (memoryManager) URIErrorObject(this, message); -} - -Object *ExecutionEngine::newVariantObject(const QVariant &v) -{ - return new (memoryManager) VariantObject(this, v); -} - -Object *ExecutionEngine::newForEachIteratorObject(ExecutionContext *ctx, Object *o) -{ - return new (memoryManager) ForEachIteratorObject(ctx, o); -} - -Object *ExecutionEngine::qmlContextObject() const -{ - ExecutionContext *ctx = current; - - if (ctx->type == QV4::ExecutionContext::Type_SimpleCallContext) - ctx = ctx->parent; - - if (!ctx->outer) - return 0; - - while (ctx->outer && ctx->outer->type != ExecutionContext::Type_GlobalContext) - ctx = ctx->outer; - - assert(ctx); - if (ctx->type != ExecutionContext::Type_QmlContext) - return 0; - - return static_cast(ctx)->activation; -} - -namespace { - struct LineNumberResolver { - const ExecutionEngine* engine; - QScopedPointer nativeTrace; - - LineNumberResolver(const ExecutionEngine *engine) - : engine(engine) - { - } - - void resolve(ExecutionEngine::StackFrame *frame, ExecutionContext *context, Function *function) - { - if (context->interpreterInstructionPointer) { - qptrdiff offset = *context->interpreterInstructionPointer - 1 - function->codeData; - frame->line = function->lineNumberForProgramCounter(offset); - } else { - if (!nativeTrace) - nativeTrace.reset(new QV4::NativeStackTrace(engine->current)); - - NativeFrame nativeFrame = nativeTrace->nextFrame(); - if (nativeFrame.function == function) - frame->line = nativeFrame.line; - } - } - }; -} - -QVector ExecutionEngine::stackTrace(int frameLimit) const -{ - LineNumberResolver lineNumbers(this); - - QVector stack; - - QV4::ExecutionContext *c = current; - while (c && frameLimit) { - if (CallContext *callCtx = c->asCallContext()) { - StackFrame frame; - if (callCtx->function->function) - frame.source = callCtx->function->function->sourceFile; - frame.function = callCtx->function->name->toQString(); - frame.line = -1; - frame.column = -1; - - if (callCtx->function->function) - lineNumbers.resolve(&frame, callCtx, callCtx->function->function); - - stack.append(frame); - --frameLimit; - } - c = c->parent; - } - - if (frameLimit && globalCode) { - StackFrame frame; - frame.source = globalCode->sourceFile; - frame.function = globalCode->name->toQString(); - frame.line = -1; - frame.column = -1; - - lineNumbers.resolve(&frame, rootContext, globalCode); - - stack.append(frame); - } - return stack; -} - -ExecutionEngine::StackFrame ExecutionEngine::currentStackFrame() const -{ - StackFrame frame; - frame.line = -1; - frame.column = -1; - - QVector trace = stackTrace(/*limit*/ 1); - if (!trace.isEmpty()) - frame = trace.first(); - - return frame; -} - -QUrl ExecutionEngine::resolvedUrl(const QString &file) -{ - QUrl src(file); - if (!src.isRelative()) - return src; - - QUrl base; - QV4::ExecutionContext *c = current; - while (c) { - if (CallContext *callCtx = c->asCallContext()) { - if (callCtx->function->function) - base.setUrl(callCtx->function->function->sourceFile); - break; - } - c = c->parent; - } - - if (base.isEmpty() && globalCode) - base.setUrl(globalCode->sourceFile); - - if (base.isEmpty()) - return src; - - return base.resolved(src); -} - -void ExecutionEngine::requireArgumentsAccessors(int n) -{ - if (n <= argumentsAccessors.size()) - return; - - uint oldSize = argumentsAccessors.size(); - argumentsAccessors.resize(n); - for (int i = oldSize; i < n; ++i) { - FunctionObject *get = new (memoryManager) ArgumentsGetterFunction(rootContext, i); - get->prototype = functionPrototype; - FunctionObject *set = new (memoryManager) ArgumentsSetterFunction(rootContext, i); - set->prototype = functionPrototype; - Property pd = Property::fromAccessor(get, set); - argumentsAccessors[i] = pd; - } -} - -void ExecutionEngine::markObjects() -{ - identifierTable->mark(); - - globalObject->mark(); - - if (globalCode) - globalCode->mark(); - - for (int i = 0; i < argumentsAccessors.size(); ++i) { - const Property &pd = argumentsAccessors.at(i); - if (FunctionObject *getter = pd.getter()) - getter->mark(); - if (FunctionObject *setter = pd.setter()) - setter->mark(); - } - - ExecutionContext *c = current; - while (c) { - c->mark(); - c = c->parent; - } - - for (int i = 0; i < functions.size(); ++i) - functions.at(i)->mark(); - - id_length->mark(); - id_prototype->mark(); - id_constructor->mark(); - id_arguments->mark(); - id_caller->mark(); - id_this->mark(); - id___proto__->mark(); - id_enumerable->mark(); - id_configurable->mark(); - id_writable->mark(); - id_value->mark(); - id_get->mark(); - id_set->mark(); - id_eval->mark(); - id_uintMax->mark(); - id_name->mark(); - - objectCtor.mark(); - stringCtor.mark(); - numberCtor.mark(); - booleanCtor.mark(); - arrayCtor.mark(); - functionCtor.mark(); - dateCtor.mark(); - regExpCtor.mark(); - errorCtor.mark(); - evalErrorCtor.mark(); - rangeErrorCtor.mark(); - referenceErrorCtor.mark(); - syntaxErrorCtor.mark(); - typeErrorCtor.mark(); - uRIErrorCtor.mark(); - - objectPrototype->mark(); - stringPrototype->mark(); - numberPrototype->mark(); - booleanPrototype->mark(); - arrayPrototype->mark(); - functionPrototype->mark(); - datePrototype->mark(); - regExpPrototype->mark(); - errorPrototype->mark(); - evalErrorPrototype->mark(); - rangeErrorPrototype->mark(); - referenceErrorPrototype->mark(); - syntaxErrorPrototype->mark(); - typeErrorPrototype->mark(); - uRIErrorPrototype->mark(); - - variantPrototype->mark(); - sequencePrototype->mark(); - - if (m_qmlExtensions) - m_qmlExtensions->markObjects(); -} - -namespace { - bool functionSortHelper(Function *lhs, Function *rhs) - { - return reinterpret_cast(lhs->code) < reinterpret_cast(rhs->code); - } - - struct FindHelper - { - bool operator()(Function *function, quintptr pc) - { - return reinterpret_cast(function->code) < pc - && (reinterpret_cast(function->code) + function->codeSize) < pc; - } - - bool operator()(quintptr pc, Function *function) - { - return pc < reinterpret_cast(function->code); - } - }; -} - -Function *ExecutionEngine::functionForProgramCounter(quintptr pc) const -{ - if (functionsNeedSort) { - qSort(functions.begin(), functions.end(), functionSortHelper); - functionsNeedSort = false; - } - - QVector::ConstIterator it = qBinaryFind(functions.constBegin(), functions.constEnd(), - pc, FindHelper()); - if (it != functions.constEnd()) - return *it; - return 0; -} - -QmlExtensions *ExecutionEngine::qmlExtensions() -{ - if (!m_qmlExtensions) - m_qmlExtensions = new QmlExtensions; - return m_qmlExtensions; -} - -QT_END_NAMESPACE diff --git a/src/qml/qml/v4/qv4engine_p.h b/src/qml/qml/v4/qv4engine_p.h deleted file mode 100644 index 20fbf03ae2..0000000000 --- a/src/qml/qml/v4/qv4engine_p.h +++ /dev/null @@ -1,332 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QV4ENGINE_H -#define QV4ENGINE_H - -#include "qv4global_p.h" -#include "qv4isel_p.h" -#include "qv4util_p.h" -#include "qv4context_p.h" -#include "qv4property_p.h" -#include - -namespace WTF { -class BumpPointerAllocator; -} - -QT_BEGIN_NAMESPACE - -class QV8Engine; - -namespace QV4 { -namespace Debugging { -class Debugger; -} // namespace Debugging -} - -namespace QV4 { - -struct Function; -struct Object; -struct BooleanObject; -struct NumberObject; -struct StringObject; -struct ArrayObject; -struct DateObject; -struct FunctionObject; -struct BoundFunction; -struct RegExpObject; -struct ErrorObject; -struct ArgumentsObject; -struct ExecutionContext; -struct ExecutionEngine; -class MemoryManager; -class UnwindHelper; -class ExecutableAllocator; - -struct ObjectPrototype; -struct StringPrototype; -struct NumberPrototype; -struct BooleanPrototype; -struct ArrayPrototype; -struct FunctionPrototype; -struct DatePrototype; -struct RegExpPrototype; -struct ErrorPrototype; -struct EvalErrorPrototype; -struct RangeErrorPrototype; -struct ReferenceErrorPrototype; -struct SyntaxErrorPrototype; -struct TypeErrorPrototype; -struct URIErrorPrototype; -struct VariantPrototype; -struct SequencePrototype; -struct EvalFunction; -struct IdentifierTable; -struct InternalClass; -class MultiplyWrappedQObjectMap; -class RegExp; -class RegExpCache; -struct QmlExtensions; - -struct Q_QML_EXPORT ExecutionEngine -{ - MemoryManager *memoryManager; - ExecutableAllocator *executableAllocator; - ExecutableAllocator *regExpAllocator; - QScopedPointer iselFactory; - - ExecutionContext *current; - GlobalContext *rootContext; - - WTF::BumpPointerAllocator *bumperPointerAllocator; // Used by Yarr Regex engine. - - IdentifierTable *identifierTable; - - QV4::Debugging::Debugger *debugger; - - Object *globalObject; - - Function *globalCode; - - QV8Engine *v8Engine; - - Value objectCtor; - Value stringCtor; - Value numberCtor; - Value booleanCtor; - Value arrayCtor; - Value functionCtor; - Value dateCtor; - Value regExpCtor; - Value errorCtor; - Value evalErrorCtor; - Value rangeErrorCtor; - Value referenceErrorCtor; - Value syntaxErrorCtor; - Value typeErrorCtor; - Value uRIErrorCtor; - - ObjectPrototype *objectPrototype; - StringPrototype *stringPrototype; - NumberPrototype *numberPrototype; - BooleanPrototype *booleanPrototype; - ArrayPrototype *arrayPrototype; - FunctionPrototype *functionPrototype; - DatePrototype *datePrototype; - RegExpPrototype *regExpPrototype; - ErrorPrototype *errorPrototype; - EvalErrorPrototype *evalErrorPrototype; - RangeErrorPrototype *rangeErrorPrototype; - ReferenceErrorPrototype *referenceErrorPrototype; - SyntaxErrorPrototype *syntaxErrorPrototype; - TypeErrorPrototype *typeErrorPrototype; - URIErrorPrototype *uRIErrorPrototype; - - VariantPrototype *variantPrototype; - SequencePrototype *sequencePrototype; - - QQmlJS::MemoryPool classPool; - InternalClass *emptyClass; - InternalClass *arrayClass; - - EvalFunction *evalFunction; - - QVector argumentsAccessors; - - String *id_undefined; - String *id_null; - String *id_true; - String *id_false; - String *id_boolean; - String *id_number; - String *id_string; - String *id_object; - String *id_function; - String *id_length; - String *id_prototype; - String *id_constructor; - String *id_arguments; - String *id_caller; - String *id_this; - String *id___proto__; - String *id_enumerable; - String *id_configurable; - String *id_writable; - String *id_value; - String *id_get; - String *id_set; - String *id_eval; - String *id_uintMax; - String *id_name; - - mutable QVector functions; - mutable bool functionsNeedSort; - - quint32 m_engineId; - - RegExpCache *regExpCache; - - // Scarce resources are "exceptionally high cost" QVariant types where allowing the - // normal JavaScript GC to clean them up is likely to lead to out-of-memory or other - // out-of-resource situations. When such a resource is passed into JavaScript we - // add it to the scarceResources list and it is destroyed when we return from the - // JavaScript execution that created it. The user can prevent this behavior by - // calling preserve() on the object which removes it from this scarceResource list. - class ScarceResourceData { - public: - ScarceResourceData(const QVariant &data) : data(data) {} - QVariant data; - QIntrusiveListNode node; - }; - QIntrusiveList scarceResources; - - // Normally the JS wrappers for QObjects are stored in the QQmlData/QObjectPrivate, - // but any time a QObject is wrapped a second time in another engine, we have to do - // bookkeeping. - MultiplyWrappedQObjectMap *m_multiplyWrappedQObjects; - - ExecutionEngine(QQmlJS::EvalISelFactory *iselFactory = 0); - ~ExecutionEngine(); - - void enableDebugger(); - - WithContext *newWithContext(Object *with); - CatchContext *newCatchContext(String* exceptionVarName, const QV4::Value &exceptionValue); - CallContext *newCallContext(FunctionObject *f, const QV4::Value &thisObject, QV4::Value *args, int argc); - CallContext *newCallContext(void *stackSpace, FunctionObject *f, const QV4::Value &thisObject, QV4::Value *args, int argc); - CallContext *newQmlContext(FunctionObject *f, Object *qml); - ExecutionContext *pushGlobalContext(); - void pushContext(SimpleCallContext *context); - ExecutionContext *popContext(); - - Function *newFunction(const QString &name); - - FunctionObject *newBuiltinFunction(ExecutionContext *scope, String *name, Value (*code)(SimpleCallContext *)); - FunctionObject *newScriptFunction(ExecutionContext *scope, Function *function); - BoundFunction *newBoundFunction(ExecutionContext *scope, FunctionObject *target, Value boundThis, const QVector &boundArgs); - - Object *newObject(); - Object *newObject(InternalClass *internalClass); - - String *newString(const QString &s); - String *newIdentifier(const QString &text); - - Object *newStringObject(const Value &value); - Object *newNumberObject(const Value &value); - Object *newBooleanObject(const Value &value); - - ArrayObject *newArrayObject(int count = 0); - ArrayObject *newArrayObject(const QStringList &list); - - DateObject *newDateObject(const Value &value); - DateObject *newDateObject(const QDateTime &dt); - - RegExpObject *newRegExpObject(const QString &pattern, int flags); - RegExpObject *newRegExpObject(RegExp* re, bool global); - RegExpObject *newRegExpObject(const QRegExp &re); - - Object *newErrorObject(const Value &value); - Object *newSyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *message); - Object *newSyntaxErrorObject(const QString &message); - Object *newReferenceErrorObject(const QString &message); - Object *newReferenceErrorObject(const QString &message, const QString &fileName, int lineNumber); - Object *newTypeErrorObject(const QString &message); - Object *newRangeErrorObject(const QString &message); - Object *newURIErrorObject(Value message); - - Object *newVariantObject(const QVariant &v); - - Object *newForEachIteratorObject(ExecutionContext *ctx, Object *o); - - Object *qmlContextObject() const; - - struct StackFrame { - QString source; - QString function; - int line; - int column; - }; - typedef QVector StackTrace; - StackTrace stackTrace(int frameLimit = -1) const; - StackFrame currentStackFrame() const; - QUrl resolvedUrl(const QString &file); - - void requireArgumentsAccessors(int n); - - void markObjects(); - - void initRootContext(); - - InternalClass *newClass(const InternalClass &other); - - Function *functionForProgramCounter(quintptr pc) const; - - QmlExtensions *qmlExtensions(); - -private: - QmlExtensions *m_qmlExtensions; -}; - -inline void ExecutionEngine::pushContext(SimpleCallContext *context) -{ - context->parent = current; - current = context; - current->currentEvalCode = 0; -} - -inline ExecutionContext *ExecutionEngine::popContext() -{ - CallContext *c = current->asCallContext(); - if (c && !c->needsOwnArguments()) { - c->arguments = 0; - c->argumentCount = 0; - } - - current = current->parent; - return current; -} - -} // namespace QV4 - -QT_END_NAMESPACE - -#endif // QV4ENGINE_H diff --git a/src/qml/qml/v4/qv4errorobject.cpp b/src/qml/qml/v4/qv4errorobject.cpp deleted file mode 100644 index 516a4d37f8..0000000000 --- a/src/qml/qml/v4/qv4errorobject.cpp +++ /dev/null @@ -1,351 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - - -#include "qv4errorobject_p.h" -#include "qv4mm_p.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#ifndef Q_OS_WIN -# include -# ifndef Q_OS_VXWORKS -# include -# else -# include "qplatformdefs.h" -# endif -#else -# include -#endif - -using namespace QV4; - -ErrorObject::ErrorObject(ExecutionEngine *engine, const Value &message, ErrorType t) - : Object(engine) - , stack(0) -{ - type = Type_ErrorObject; - vtbl = &static_vtbl; - subtype = t; - defineAccessorProperty(engine, QStringLiteral("stack"), ErrorObject::method_get_stack, 0); - - if (!message.isUndefined()) - defineDefaultProperty(engine->newString(QStringLiteral("message")), message); - defineDefaultProperty(engine, QStringLiteral("name"), Value::fromString(engine, className())); - - stackTrace = engine->stackTrace(); - if (!stackTrace.isEmpty()) { - defineDefaultProperty(engine, QStringLiteral("fileName"), Value::fromString(engine, stackTrace.at(0).source)); - defineDefaultProperty(engine, QStringLiteral("lineNumber"), Value::fromInt32(stackTrace.at(0).line)); - } -} - -Value ErrorObject::method_get_stack(SimpleCallContext *ctx) -{ - ErrorObject *This = ctx->thisObject.asErrorObject(); - if (!This) - ctx->throwTypeError(); - if (!This->stack) { - QString trace; - for (int i = 0; i < This->stackTrace.count(); ++i) { - if (i > 0) - trace += QLatin1Char('\n'); - const ExecutionEngine::StackFrame &frame = This->stackTrace[i]; - trace += frame.function; - trace += QLatin1Char('@'); - trace += frame.source; - if (frame.line >= 0) { - trace += QLatin1Char(':'); - trace += QString::number(frame.line); - } - } - This->stack = ctx->engine->newString(trace); - } - return Value::fromString(This->stack); -} - -void ErrorObject::markObjects(Managed *that) -{ - ErrorObject *This = that->asErrorObject(); - if (This->stack) - This->stack->mark(); - Object::markObjects(that); -} - -DEFINE_MANAGED_VTABLE(ErrorObject); - -DEFINE_MANAGED_VTABLE(SyntaxErrorObject); - -SyntaxErrorObject::SyntaxErrorObject(ExecutionEngine *engine, const Value &msg) - : ErrorObject(engine, msg, SyntaxError) - , msg(0) -{ - vtbl = &static_vtbl; - prototype = engine->syntaxErrorPrototype; -} - -SyntaxErrorObject::SyntaxErrorObject(ExecutionEngine *engine, const QString &msg) - : ErrorObject(engine, Value::fromString(engine, msg), SyntaxError) - , msg(0) -{ - vtbl = &static_vtbl; - prototype = engine->syntaxErrorPrototype; -} - -SyntaxErrorObject::SyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *message) - : ErrorObject(ctx->engine, message ? Value::fromString(message->buildFullMessage(ctx)) : ctx->argument(0), SyntaxError) - , msg(message) -{ - vtbl = &static_vtbl; - prototype = ctx->engine->syntaxErrorPrototype; - if (message) { - defineDefaultProperty(ctx->engine, QStringLiteral("fileName"), Value::fromString(ctx, message->fileName)); - defineDefaultProperty(ctx->engine, QStringLiteral("lineNumber"), Value::fromInt32(message->startLine)); - } -} - - - -EvalErrorObject::EvalErrorObject(ExecutionEngine *engine, const Value &message) - : ErrorObject(engine, message, EvalError) -{ - prototype = engine->evalErrorPrototype; -} - -RangeErrorObject::RangeErrorObject(ExecutionEngine *engine, const Value &message) - : ErrorObject(engine, message, RangeError) -{ - prototype = engine->rangeErrorPrototype; -} - -RangeErrorObject::RangeErrorObject(ExecutionEngine *engine, const QString &message) - : ErrorObject(engine, Value::fromString(engine, message), RangeError) -{ - prototype = engine->rangeErrorPrototype; -} - -ReferenceErrorObject::ReferenceErrorObject(ExecutionEngine *engine, const Value &message) - : ErrorObject(engine, message, ReferenceError) -{ - prototype = engine->referenceErrorPrototype; -} - -ReferenceErrorObject::ReferenceErrorObject(ExecutionEngine *engine, const QString &message) - : ErrorObject(engine, Value::fromString(engine, message), ReferenceError) -{ - prototype = engine->referenceErrorPrototype; -} - -ReferenceErrorObject::ReferenceErrorObject(ExecutionEngine *engine, const QString &msg, const QString &fileName, int lineNumber) - : ErrorObject(engine, Value::fromString(engine, msg), ReferenceError) -{ - prototype = engine->referenceErrorPrototype; - defineDefaultProperty(engine, QStringLiteral("fileName"), Value::fromString(engine->rootContext, fileName)); - defineDefaultProperty(engine, QStringLiteral("lineNumber"), Value::fromInt32(lineNumber)); -} - -TypeErrorObject::TypeErrorObject(ExecutionEngine *engine, const Value &message) - : ErrorObject(engine, message, TypeError) -{ - prototype = engine->typeErrorPrototype; -} - -TypeErrorObject::TypeErrorObject(ExecutionEngine *engine, const QString &message) - : ErrorObject(engine, Value::fromString(engine, message), TypeError) -{ - prototype = engine->typeErrorPrototype; -} - -URIErrorObject::URIErrorObject(ExecutionEngine *engine, const Value &message) - : ErrorObject(engine, message, URIError) -{ - prototype = engine->uRIErrorPrototype; -} - -DEFINE_MANAGED_VTABLE(ErrorCtor); -DEFINE_MANAGED_VTABLE(EvalErrorCtor); -DEFINE_MANAGED_VTABLE(RangeErrorCtor); -DEFINE_MANAGED_VTABLE(ReferenceErrorCtor); -DEFINE_MANAGED_VTABLE(SyntaxErrorCtor); -DEFINE_MANAGED_VTABLE(TypeErrorCtor); -DEFINE_MANAGED_VTABLE(URIErrorCtor); - -ErrorCtor::ErrorCtor(ExecutionContext *scope) - : FunctionObject(scope, scope->engine->newIdentifier(QStringLiteral("Error"))) -{ - vtbl = &static_vtbl; -} - -ErrorCtor::ErrorCtor(ExecutionContext *scope, String *name) - : FunctionObject(scope, name) -{ - vtbl = &static_vtbl; -} - -Value ErrorCtor::construct(Managed *m, Value *args, int argc) -{ - return Value::fromObject(m->engine()->newErrorObject(argc ? args[0] : Value::undefinedValue())); -} - -Value ErrorCtor::call(Managed *that, const Value &, Value *args, int argc) -{ - return that->construct(args, argc); -} - -EvalErrorCtor::EvalErrorCtor(ExecutionContext *scope) - : ErrorCtor(scope, scope->engine->newIdentifier("EvalError")) -{ - vtbl = &static_vtbl; -} - -Value EvalErrorCtor::construct(Managed *m, Value *args, int argc) -{ - return Value::fromObject(new (m->engine()->memoryManager) EvalErrorObject(m->engine(), argc ? args[0] : Value::undefinedValue())); -} - -RangeErrorCtor::RangeErrorCtor(ExecutionContext *scope) - : ErrorCtor(scope, scope->engine->newIdentifier("RangeError")) -{ - vtbl = &static_vtbl; -} - -Value RangeErrorCtor::construct(Managed *m, Value *args, int argc) -{ - return Value::fromObject(new (m->engine()->memoryManager) RangeErrorObject(m->engine(), argc ? args[0] : Value::undefinedValue())); -} - -ReferenceErrorCtor::ReferenceErrorCtor(ExecutionContext *scope) - : ErrorCtor(scope, scope->engine->newIdentifier("ReferenceError")) -{ - vtbl = &static_vtbl; -} - -Value ReferenceErrorCtor::construct(Managed *m, Value *args, int argc) -{ - return Value::fromObject(new (m->engine()->memoryManager) ReferenceErrorObject(m->engine(), argc ? args[0] : Value::undefinedValue())); -} - -SyntaxErrorCtor::SyntaxErrorCtor(ExecutionContext *scope) - : ErrorCtor(scope, scope->engine->newIdentifier("SyntaxError")) -{ - vtbl = &static_vtbl; -} - -Value SyntaxErrorCtor::construct(Managed *m, Value *args, int argc) -{ - return Value::fromObject(new (m->engine()->memoryManager) SyntaxErrorObject(m->engine(), argc ? args[0] : Value::undefinedValue())); -} - -TypeErrorCtor::TypeErrorCtor(ExecutionContext *scope) - : ErrorCtor(scope, scope->engine->newIdentifier("TypeError")) -{ - vtbl = &static_vtbl; -} - -Value TypeErrorCtor::construct(Managed *m, Value *args, int argc) -{ - return Value::fromObject(new (m->engine()->memoryManager) TypeErrorObject(m->engine(), argc ? args[0] : Value::undefinedValue())); -} - -URIErrorCtor::URIErrorCtor(ExecutionContext *scope) - : ErrorCtor(scope, scope->engine->newIdentifier("URIError")) -{ - vtbl = &static_vtbl; -} - -Value URIErrorCtor::construct(Managed *m, Value *args, int argc) -{ - return Value::fromObject(new (m->engine()->memoryManager) URIErrorObject(m->engine(), argc ? args[0] : Value::undefinedValue())); -} - -void ErrorPrototype::init(ExecutionEngine *engine, const Value &ctor, Object *obj) -{ - ctor.objectValue()->defineReadonlyProperty(engine->id_prototype, Value::fromObject(obj)); - ctor.objectValue()->defineReadonlyProperty(engine->id_length, Value::fromInt32(1)); - obj->defineDefaultProperty(engine, QStringLiteral("constructor"), ctor); - obj->defineDefaultProperty(engine, QStringLiteral("toString"), method_toString, 0); - obj->defineDefaultProperty(engine, QStringLiteral("message"), Value::fromString(engine, QString())); -} - -Value ErrorPrototype::method_toString(SimpleCallContext *ctx) -{ - Object *o = ctx->thisObject.asObject(); - if (!o) - ctx->throwTypeError(); - - Value name = o->get(ctx->engine->newString(QString::fromLatin1("name"))); - QString qname; - if (name.isUndefined()) - qname = QString::fromLatin1("Error"); - else - qname = __qmljs_to_string(name, ctx).stringValue()->toQString(); - - Value message = o->get(ctx->engine->newString(QString::fromLatin1("message"))); - QString qmessage; - if (!message.isUndefined()) - qmessage = __qmljs_to_string(message, ctx).stringValue()->toQString(); - - QString str; - if (qname.isEmpty()) { - str = qmessage; - } else if (qmessage.isEmpty()) { - str = qname; - } else { - str = qname + QLatin1String(": ") + qmessage; - } - - return Value::fromString(ctx, str); -} diff --git a/src/qml/qml/v4/qv4errorobject_p.h b/src/qml/qml/v4/qv4errorobject_p.h deleted file mode 100644 index d3e0f107bc..0000000000 --- a/src/qml/qml/v4/qv4errorobject_p.h +++ /dev/null @@ -1,246 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QV4ERROROBJECT_H -#define QV4ERROROBJECT_H - -#include "qv4object_p.h" -#include "qv4functionobject_p.h" - -QT_BEGIN_NAMESPACE - -namespace QV4 { - -struct SyntaxErrorObject; - -struct ErrorObject: Object { - Q_MANAGED - - enum ErrorType { - Error, - EvalError, - RangeError, - ReferenceError, - SyntaxError, - TypeError, - URIError - }; - - ErrorObject(ExecutionEngine *engine, const Value &message, ErrorType t = Error); - - SyntaxErrorObject *asSyntaxError(); - - ExecutionEngine::StackTrace stackTrace; - String *stack; - - static Value method_get_stack(SimpleCallContext *ctx); - static void markObjects(Managed *that); - static void destroy(Managed *that) { static_cast(that)->~ErrorObject(); } -}; - -struct EvalErrorObject: ErrorObject { - EvalErrorObject(ExecutionEngine *engine, const Value &message); -}; - -struct RangeErrorObject: ErrorObject { - RangeErrorObject(ExecutionEngine *engine, const Value &message); - RangeErrorObject(ExecutionEngine *engine, const QString &msg); -}; - -struct ReferenceErrorObject: ErrorObject { - ReferenceErrorObject(ExecutionEngine *engine, const Value &message); - ReferenceErrorObject(ExecutionEngine *engine, const QString &msg); - ReferenceErrorObject(ExecutionEngine *engine, const QString &msg, const QString &fileName, int lineNumber); -}; - -struct SyntaxErrorObject: ErrorObject { - SyntaxErrorObject(ExecutionEngine *engine, const Value &msg); - SyntaxErrorObject(ExecutionEngine *engine, const QString &msg); - SyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *msg); - ~SyntaxErrorObject() { delete msg; } - static void destroy(Managed *that) { static_cast(that)->~SyntaxErrorObject(); } - - DiagnosticMessage *message() { return msg; } - -private: - DiagnosticMessage *msg; -protected: - static const ManagedVTable static_vtbl; -}; - -struct TypeErrorObject: ErrorObject { - TypeErrorObject(ExecutionEngine *engine, const Value &message); - TypeErrorObject(ExecutionEngine *engine, const QString &msg); -}; - -struct URIErrorObject: ErrorObject { - URIErrorObject(ExecutionEngine *engine, const Value &message); -}; - -struct ErrorCtor: FunctionObject -{ - ErrorCtor(ExecutionContext *scope); - ErrorCtor(ExecutionContext *scope, String *name); - - static Value construct(Managed *, Value *args, int argc); - static Value call(Managed *that, const Value &, Value *, int); - -protected: - static const ManagedVTable static_vtbl; -}; - -struct EvalErrorCtor: ErrorCtor -{ - EvalErrorCtor(ExecutionContext *scope); - - static Value construct(Managed *m, Value *args, int argc); - -protected: - static const ManagedVTable static_vtbl; -}; - -struct RangeErrorCtor: ErrorCtor -{ - RangeErrorCtor(ExecutionContext *scope); - - static Value construct(Managed *m, Value *args, int argc); - -protected: - static const ManagedVTable static_vtbl; -}; - -struct ReferenceErrorCtor: ErrorCtor -{ - ReferenceErrorCtor(ExecutionContext *scope); - - static Value construct(Managed *m, Value *args, int argc); - -protected: - static const ManagedVTable static_vtbl; -}; - -struct SyntaxErrorCtor: ErrorCtor -{ - SyntaxErrorCtor(ExecutionContext *scope); - - static Value construct(Managed *m, Value *args, int argc); - -protected: - static const ManagedVTable static_vtbl; -}; - -struct TypeErrorCtor: ErrorCtor -{ - TypeErrorCtor(ExecutionContext *scope); - - static Value construct(Managed *m, Value *args, int argc); - -protected: - static const ManagedVTable static_vtbl; -}; - -struct URIErrorCtor: ErrorCtor -{ - URIErrorCtor(ExecutionContext *scope); - - static Value construct(Managed *m, Value *args, int argc); - -protected: - static const ManagedVTable static_vtbl; -}; - - -struct ErrorPrototype: ErrorObject -{ - // ### shouldn't be undefined - ErrorPrototype(ExecutionEngine *engine): ErrorObject(engine, Value::undefinedValue()) {} - void init(ExecutionEngine *engine, const Value &ctor) { init(engine, ctor, this); } - - static void init(ExecutionEngine *engine, const Value &ctor, Object *obj); - static Value method_toString(SimpleCallContext *ctx); -}; - -struct EvalErrorPrototype: EvalErrorObject -{ - EvalErrorPrototype(ExecutionEngine *engine): EvalErrorObject(engine, Value::undefinedValue()) { vtbl = &static_vtbl; } - void init(ExecutionEngine *engine, const Value &ctor) { ErrorPrototype::init(engine, ctor, this); } -}; - -struct RangeErrorPrototype: RangeErrorObject -{ - RangeErrorPrototype(ExecutionEngine *engine): RangeErrorObject(engine, Value::undefinedValue()) { vtbl = &static_vtbl; } - void init(ExecutionEngine *engine, const Value &ctor) { ErrorPrototype::init(engine, ctor, this); } -}; - -struct ReferenceErrorPrototype: ReferenceErrorObject -{ - ReferenceErrorPrototype(ExecutionEngine *engine): ReferenceErrorObject(engine, Value::undefinedValue()) { vtbl = &static_vtbl; } - void init(ExecutionEngine *engine, const Value &ctor) { ErrorPrototype::init(engine, ctor, this); } -}; - -struct SyntaxErrorPrototype: SyntaxErrorObject -{ - SyntaxErrorPrototype(ExecutionEngine *engine): SyntaxErrorObject(engine, 0) { vtbl = &static_vtbl; } - void init(ExecutionEngine *engine, const Value &ctor) { ErrorPrototype::init(engine, ctor, this); } -}; - -struct TypeErrorPrototype: TypeErrorObject -{ - TypeErrorPrototype(ExecutionEngine *engine): TypeErrorObject(engine, Value::undefinedValue()) { vtbl = &static_vtbl; } - void init(ExecutionEngine *engine, const Value &ctor) { ErrorPrototype::init(engine, ctor, this); } -}; - -struct URIErrorPrototype: URIErrorObject -{ - URIErrorPrototype(ExecutionEngine *engine): URIErrorObject(engine, Value::undefinedValue()) { vtbl = &static_vtbl; } - void init(ExecutionEngine *engine, const Value &ctor) { ErrorPrototype::init(engine, ctor, this); } -}; - - -inline SyntaxErrorObject *ErrorObject::asSyntaxError() -{ - return subtype == SyntaxError ? static_cast(this) : 0; -} - -} - -QT_END_NAMESPACE - -#endif // QV4ECMAOBJECTS_P_H diff --git a/src/qml/qml/v4/qv4exception.cpp b/src/qml/qml/v4/qv4exception.cpp deleted file mode 100644 index 9f15c27ffc..0000000000 --- a/src/qml/qml/v4/qv4exception.cpp +++ /dev/null @@ -1,129 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4exception_p.h" -#include "qv4errorobject_p.h" -#include "qv4debugging_p.h" -#include "qv4unwindhelper_p.h" - -#include - -#if USE(LIBUNWIND_DEBUG) -#include -#include -#endif - -QT_BEGIN_NAMESPACE - -using namespace QV4; - - -void Exception::throwException(ExecutionContext *context, const Value &value) -{ - if (context->engine->debugger) - context->engine->debugger->aboutToThrow(value); - - UnwindHelper::prepareForUnwind(context); - -#if USE(LIBUNWIND_DEBUG) - printf("about to throw exception. walking stack first with libunwind:\n"); - unw_cursor_t cursor; unw_context_t uc; - unw_word_t ip, sp; - - unw_getcontext(&uc); - unw_init_local(&cursor, &uc); - while (unw_step(&cursor) > 0) { - unw_get_reg(&cursor, UNW_REG_IP, &ip); - unw_get_reg(&cursor, UNW_REG_SP, &sp); - printf("ip = %lx, sp = %lx ", (long) ip, (long) sp); - void * const addr = (void*)ip; - char **symbol = backtrace_symbols(&addr, 1); - printf("%s", symbol[0]); - free(symbol); - printf("\n"); - } - printf("stack walked. throwing exception now...\n"); -#endif - - throwInternal(context, value); -} - -Exception::Exception(ExecutionContext *throwingContext, const Value &exceptionValue) - : exception(exceptionValue) -{ - this->throwingContext = throwingContext->engine->current; - accepted = false; - if (ErrorObject *error = exceptionValue.asErrorObject()) - m_stackTrace = error->stackTrace; - else - m_stackTrace = throwingContext->engine->stackTrace(); -} - -Exception::~Exception() -{ - assert(accepted); -} - -void Exception::accept(ExecutionContext *catchingContext) -{ - assert(!accepted); - accepted = true; - partiallyUnwindContext(catchingContext); -} - -void Exception::partiallyUnwindContext(ExecutionContext *catchingContext) -{ - if (!throwingContext) - return; - ExecutionContext *context = throwingContext; - while (context != catchingContext) - context = context->engine->popContext(); - throwingContext = context; -} - -#if !defined(V4_CXX_ABI_EXCEPTION) -void Exception::throwInternal(ExecutionContext *throwingContext, const Value &exceptionValue) -{ - throw Exception(throwingContext, exceptionValue); -} -#endif - -QT_END_NAMESPACE diff --git a/src/qml/qml/v4/qv4exception_gcc.cpp b/src/qml/qml/v4/qv4exception_gcc.cpp deleted file mode 100644 index 0324a06e0b..0000000000 --- a/src/qml/qml/v4/qv4exception_gcc.cpp +++ /dev/null @@ -1,143 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4exception_p.h" - -#include -#include -#include -#include -#include - -/* - * This is a little bit hacky as it relies on the fact that exceptions are - * reference counted in libstdc++ and that affects the layout of the standardized - * cxa_exception, making it bigger. LLVM's libcxxabi stores the reference count - * differently, so this here is entirely GNU libstdc++ specific. - * - * Eliminating this dependency is doable but requires replacing the use of C++ exceptions - * with foreign exceptions (a different exception class) and then using __cxa_get_globals - * to get hold of the exception inside the catch (...). AFAICS that would be portable. - */ - -namespace { - -// 2.1.1 from http://mentorembedded.github.io/cxx-abi/abi-eh.html -struct cxa_exception { - std::type_info *typeInfo; - void (*exceptionDestructor)(void*); - std::unexpected_handler unexpectedHandler; - std::terminate_handler terminateHandler; - cxa_exception *nextException; - int handlerCount; -#ifdef __ARM_EABI_UNWINDER__ - cxa_exception *nextPropagatingException; - int propagationCount; -#else - int handlerSwitchValue; - const char *actionRecord; - const char *languageSpecificData; - void *catchTemp; - void *adjustedPtr; -#endif - _Unwind_Exception unwindHeader; -}; - -// This is what libstdc++ actually allocates -struct gcc_refcounted_compatible_exception { - _Atomic_word refCount; - cxa_exception x; -}; - -} - -static void exception_cleanup(_Unwind_Reason_Code, _Unwind_Exception *ex) -{ - gcc_refcounted_compatible_exception *exception = reinterpret_cast(ex + 1) - 1; - if (!--exception->refCount) { - if (exception->x.exceptionDestructor) - exception->x.exceptionDestructor(ex + 1); - abi::__cxa_free_exception(ex + 1); - } -} - -static void exception_destructor(void *ex) -{ - reinterpret_cast(ex)->~Exception(); -} - -QT_BEGIN_NAMESPACE - -using namespace QV4; - -void Exception::throwInternal(ExecutionContext *throwingContext, const Value &exceptionValue) -{ - void *rawException = abi::__cxa_allocate_exception(sizeof(QV4::Exception)); - gcc_refcounted_compatible_exception *refCountedException = reinterpret_cast(rawException) - 1; - cxa_exception *exception = &refCountedException->x; - - (void)new (rawException) Exception(throwingContext, exceptionValue); - - refCountedException->refCount = 1; - exception->typeInfo = const_cast(&typeid(Exception)); - exception->exceptionDestructor = &exception_destructor; - exception->unexpectedHandler = std::unexpected; - exception->terminateHandler = std::terminate; - exception->unwindHeader.exception_cleanup = &exception_cleanup; -#ifdef __ARM_EABI_UNWINDER__ - exception->unwindHeader.exception_class[0] = 'G'; - exception->unwindHeader.exception_class[1] = 'N'; - exception->unwindHeader.exception_class[2] = 'U'; - exception->unwindHeader.exception_class[3] = 'C'; - exception->unwindHeader.exception_class[4] = 'C'; - exception->unwindHeader.exception_class[5] = '+'; - exception->unwindHeader.exception_class[6] = '+'; - exception->unwindHeader.exception_class[7] = 0; -#else - exception->unwindHeader.exception_class = 0x474e5543432b2b00; // GNUCC++0 -#endif - - _Unwind_RaiseException(&exception->unwindHeader); - abi::__cxa_begin_catch(rawException); - std::terminate(); -} - -QT_END_NAMESPACE diff --git a/src/qml/qml/v4/qv4exception_p.h b/src/qml/qml/v4/qv4exception_p.h deleted file mode 100644 index 8ba06f57f4..0000000000 --- a/src/qml/qml/v4/qv4exception_p.h +++ /dev/null @@ -1,81 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QV4EXCEPTION_GNU_P -#define QV4EXCEPTION_GNU_P - -#include -#include "qv4value_p.h" -#include "qv4engine_p.h" - -QT_BEGIN_NAMESPACE - -namespace QV4 { - -struct Q_QML_EXPORT Exception { - static void throwException(ExecutionContext *throwingContext, const Value &exceptionValue); - - ~Exception(); - - void accept(ExecutionContext *catchingContext); - - void partiallyUnwindContext(ExecutionContext *catchingContext); - - Value value() const { return exception; } - - ExecutionEngine::StackTrace stackTrace() const { return m_stackTrace; } - -private: - void *operator new(size_t, void *p) { return p; } - - explicit Exception(ExecutionContext *throwingContext, const Value &exceptionValue); - - ExecutionContext *throwingContext; - bool accepted; - PersistentValue exception; - ExecutionEngine::StackTrace m_stackTrace; - static void throwInternal(ExecutionContext *throwingContext, const Value &exceptionValue); -}; - -} // namespace QV4 - -QT_END_NAMESPACE - -#endif // QV4EXCEPTION_GNU_P diff --git a/src/qml/qml/v4/qv4executableallocator.cpp b/src/qml/qml/v4/qv4executableallocator.cpp deleted file mode 100644 index a754663556..0000000000 --- a/src/qml/qml/v4/qv4executableallocator.cpp +++ /dev/null @@ -1,220 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4executableallocator_p.h" - -#include -#include -#include - -using namespace QV4; - -void *ExecutableAllocator::Allocation::start() const -{ - return reinterpret_cast(addr); -} - -ExecutableAllocator::Allocation *ExecutableAllocator::Allocation::split(size_t dividingSize) -{ - Allocation *remainder = new Allocation; - if (next) - next->prev = remainder; - - remainder->next = next; - next = remainder; - - remainder->prev = this; - - remainder->size = size - dividingSize; - remainder->free = free; - remainder->addr = addr + dividingSize; - size = dividingSize; - - return remainder; -} - -bool ExecutableAllocator::Allocation::mergeNext(ExecutableAllocator *allocator) -{ - assert(free); - if (!next || !next->free) - return false; - - allocator->freeAllocations.remove(size, this); - allocator->freeAllocations.remove(next->size, next); - - size += next->size; - Allocation *newNext = next->next; - delete next; - next = newNext; - if (next) - next->prev = this; - - allocator->freeAllocations.insert(size, this); - return true; -} - -bool ExecutableAllocator::Allocation::mergePrevious(ExecutableAllocator *allocator) -{ - assert(free); - if (!prev || !prev->free) - return false; - - allocator->freeAllocations.remove(size, this); - allocator->freeAllocations.remove(prev->size, prev); - - prev->size += size; - if (next) - next->prev = prev; - prev->next = next; - - allocator->freeAllocations.insert(prev->size, prev); - - delete this; - return true; -} - -ExecutableAllocator::ChunkOfPages::~ChunkOfPages() -{ - delete unwindInfo; - Allocation *alloc = firstAllocation; - while (alloc) { - Allocation *next = alloc->next; - delete alloc; - alloc = next; - } - pages->deallocate(); - delete pages; -} - -bool ExecutableAllocator::ChunkOfPages::contains(Allocation *alloc) const -{ - Allocation *it = firstAllocation; - while (it) { - if (it == alloc) - return true; - it = it->next; - } - return false; -} - -ExecutableAllocator::~ExecutableAllocator() -{ - qDeleteAll(chunks); -} - -ExecutableAllocator::Allocation *ExecutableAllocator::allocate(size_t size) -{ - Allocation *allocation = 0; - - // Code is best aligned to 16-byte boundaries. - size = WTF::roundUpToMultipleOf(16, size); - - QMultiMap::Iterator it = freeAllocations.lowerBound(size); - if (it != freeAllocations.end()) { - allocation = *it; - freeAllocations.erase(it); - } - - if (!allocation) { - ChunkOfPages *chunk = new ChunkOfPages; - size_t allocSize = WTF::roundUpToMultipleOf(WTF::pageSize(), size); - chunk->pages = new WTF::PageAllocation(WTF::PageAllocation::allocate(allocSize, OSAllocator::JSJITCodePages)); - chunks.insert(reinterpret_cast(chunk->pages->base()) - 1, chunk); - allocation = new Allocation; - allocation->addr = reinterpret_cast(chunk->pages->base()); - allocation->size = allocSize; - allocation->free = true; - chunk->firstAllocation = allocation; - } - - assert(allocation); - assert(allocation->free); - - allocation->free = false; - - if (allocation->size > size) { - Allocation *remainder = allocation->split(size); - remainder->free = true; - if (!remainder->mergeNext(this)) - freeAllocations.insert(remainder->size, remainder); - } - - return allocation; -} - -void ExecutableAllocator::free(Allocation *allocation) -{ - assert(allocation); - - allocation->free = true; - - QMap::Iterator it = chunks.lowerBound(allocation->addr); - if (it != chunks.begin()) - --it; - assert(it != chunks.end()); - ChunkOfPages *chunk = *it; - assert(chunk->contains(allocation)); - - bool merged = allocation->mergeNext(this); - merged |= allocation->mergePrevious(this); - if (!merged) - freeAllocations.insert(allocation->size, allocation); - - allocation = 0; - - if (!chunk->firstAllocation->next) { - freeAllocations.remove(chunk->firstAllocation->size, chunk->firstAllocation); - chunks.erase(it); - delete chunk; - return; - } -} - -ExecutableAllocator::ChunkOfPages *ExecutableAllocator::chunkForAllocation(Allocation *allocation) const -{ - QMap::ConstIterator it = chunks.lowerBound(allocation->addr); - if (it != chunks.begin()) - --it; - if (it == chunks.end()) - return 0; - return *it; -} - diff --git a/src/qml/qml/v4/qv4executableallocator_p.h b/src/qml/qml/v4/qv4executableallocator_p.h deleted file mode 100644 index 2a304baf9c..0000000000 --- a/src/qml/qml/v4/qv4executableallocator_p.h +++ /dev/null @@ -1,134 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QV4EXECUTABLEALLOCATOR_H -#define QV4EXECUTABLEALLOCATOR_H - -#include "qv4global_p.h" - -#include -#include -#include -#include - -namespace WTF { -class PageAllocation; -}; - -QT_BEGIN_NAMESPACE - -namespace QV4 { - -class Q_AUTOTEST_EXPORT ExecutableAllocator -{ -public: - struct ChunkOfPages; - struct Allocation; - - ~ExecutableAllocator(); - - Allocation *allocate(size_t size); - void free(Allocation *allocation); - - struct Allocation - { - Allocation() - : addr(0) - , size(0) - , free(true) - , next(0) - , prev(0) - {} - - void *start() const; - - private: - friend class ExecutableAllocator; - - Allocation *split(size_t dividingSize); - bool mergeNext(ExecutableAllocator *allocator); - bool mergePrevious(ExecutableAllocator *allocator); - - quintptr addr; - uint size : 31; // More than 2GB of function code? nah :) - uint free : 1; - Allocation *next; - Allocation *prev; - }; - - // for debugging / unit-testing - int freeAllocationCount() const { return freeAllocations.count(); } - int chunkCount() const { return chunks.count(); } - - // Used to store CIE+FDE on x86/x86-64. - struct PlatformUnwindInfo - { - virtual ~PlatformUnwindInfo() {} - }; - - struct ChunkOfPages - { - ChunkOfPages() - : pages(0) - , firstAllocation(0) - , unwindInfo(0) - {} - ~ChunkOfPages(); - - WTF::PageAllocation *pages; - Allocation *firstAllocation; - PlatformUnwindInfo *unwindInfo; - - bool contains(Allocation *alloc) const; - }; - - ChunkOfPages *chunkForAllocation(Allocation *allocation) const; - -private: - QMultiMap freeAllocations; - QMap chunks; -}; - -} - -QT_END_NAMESPACE - -#endif // QV4EXECUTABLEALLOCATOR_H diff --git a/src/qml/qml/v4/qv4function.cpp b/src/qml/qml/v4/qv4function.cpp deleted file mode 100644 index bf633a9b41..0000000000 --- a/src/qml/qml/v4/qv4function.cpp +++ /dev/null @@ -1,88 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4function_p.h" -#include "qv4managed_p.h" -#include "qv4string_p.h" -#include "qv4value_p.h" - -QT_BEGIN_NAMESPACE - -using namespace QV4; - -Function::~Function() -{ - delete[] codeData; -} - -void Function::mark() -{ - if (name) - name->mark(); - for (int i = 0; i < formals.size(); ++i) - formals.at(i)->mark(); - for (int i = 0; i < locals.size(); ++i) - locals.at(i)->mark(); - for (int i = 0; i < generatedValues.size(); ++i) - if (Managed *m = generatedValues.at(i).asManaged()) - m->mark(); - for (int i = 0; i < identifiers.size(); ++i) - identifiers.at(i)->mark(); -} - -namespace QV4 { -bool operator<(const LineNumberMapping &mapping, qptrdiff pc) -{ - return mapping.codeOffset < pc; -} -} - -int Function::lineNumberForProgramCounter(qptrdiff offset) const -{ - QVector::ConstIterator it = qLowerBound(lineNumberMappings.begin(), lineNumberMappings.end(), offset); - if (it != lineNumberMappings.constBegin() && lineNumberMappings.count() > 0) - --it; - if (it == lineNumberMappings.constEnd()) - return -1; - return it->lineNumber; -} - -QT_END_NAMESPACE diff --git a/src/qml/qml/v4/qv4function_p.h b/src/qml/qml/v4/qv4function_p.h deleted file mode 100644 index 4dd0533d51..0000000000 --- a/src/qml/qml/v4/qv4function_p.h +++ /dev/null @@ -1,142 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QV4FUNCTION_H -#define QV4FUNCTION_H - -#include "qv4global_p.h" - -#include -#include -#include - -#include -#include -#include "qv4value_def_p.h" - -QT_BEGIN_NAMESPACE - -namespace QV4 { - -struct String; -struct Function; -struct Object; -struct FunctionObject; -struct ExecutionContext; -struct ExecutionEngine; -class MemoryManager; - -struct ObjectPrototype; -struct StringPrototype; -struct NumberPrototype; -struct BooleanPrototype; -struct ArrayPrototype; -struct FunctionPrototype; -struct DatePrototype; -struct ErrorPrototype; -struct EvalErrorPrototype; -struct RangeErrorPrototype; -struct ReferenceErrorPrototype; -struct SyntaxErrorPrototype; -struct TypeErrorPrototype; -struct URIErrorPrototype; -struct InternalClass; -struct Lookup; - -struct LineNumberMapping -{ - quint32 codeOffset; - int lineNumber; -}; - -struct Function { - String *name; - - Value (*code)(ExecutionContext *, const uchar *); - const uchar *codeData; - JSC::MacroAssemblerCodeRef codeRef; - quint32 codeSize; - - QVector formals; - QVector locals; - QVector generatedValues; - QVector identifiers; - QVector nestedFunctions; - Function *outer; - - Lookup *lookups; - - bool hasNestedFunctions; - bool hasDirectEval; - bool usesArgumentsObject; - bool isStrict; - bool isNamedExpression; - - QString sourceFile; - QVector lineNumberMappings; - - Function(String *name) - : name(name) - , code(0) - , codeData(0) - , codeSize(0) - , outer(0) - , lookups(0) - , hasNestedFunctions(0) - , hasDirectEval(false) - , usesArgumentsObject(false) - , isStrict(false) - , isNamedExpression(false) - {} - ~Function(); - - inline bool needsActivation() const { return hasNestedFunctions || hasDirectEval || usesArgumentsObject; } - - void mark(); - - int lineNumberForProgramCounter(qptrdiff offset) const; -}; - -} -Q_DECLARE_TYPEINFO(QV4::LineNumberMapping, Q_PRIMITIVE_TYPE); - -QT_END_NAMESPACE - -#endif diff --git a/src/qml/qml/v4/qv4functionobject.cpp b/src/qml/qml/v4/qv4functionobject.cpp deleted file mode 100644 index ffdd08ed0a..0000000000 --- a/src/qml/qml/v4/qv4functionobject.cpp +++ /dev/null @@ -1,558 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4object_p.h" -#include "qv4jsir_p.h" -#include "qv4isel_p.h" -#include "qv4objectproto_p.h" -#include "qv4stringobject_p.h" -#include "qv4function_p.h" -#include "qv4mm_p.h" -#include "qv4exception_p.h" - -#include -#include -#include -#include -#include -#include -#include "private/qlocale_tools_p.h" - -#include -#include -#include -#include -#include -#include "qv4alloca_p.h" - -using namespace QV4; - - -DEFINE_MANAGED_VTABLE(FunctionObject); - -FunctionObject::FunctionObject(ExecutionContext *scope, String *name) - : Object(scope->engine) - , scope(scope) - , name(name) - , formalParameterList(0) - , varList(0) - , formalParameterCount(0) - , varCount(0) - , function(0) -{ - vtbl = &static_vtbl; - prototype = scope->engine->functionPrototype; - - type = Type_FunctionObject; - needsActivation = true; - usesArgumentsObject = false; - strictMode = false; -#ifndef QT_NO_DEBUG - assert(scope->next != (ExecutionContext *)0x1); -#endif - - if (name) - defineReadonlyProperty(scope->engine->id_name, Value::fromString(name)); -} - -Value FunctionObject::newInstance() -{ - return construct(0, 0); -} - -bool FunctionObject::hasInstance(Managed *that, const Value &value) -{ - FunctionObject *f = static_cast(that); - - Object *v = value.asObject(); - if (!v) - return false; - - ExecutionContext *ctx = f->engine()->current; - Object *o = f->get(ctx->engine->id_prototype).asObject(); - if (!o) - ctx->throwTypeError(); - - while (v) { - v = v->prototype; - - if (! v) - break; - else if (o == v) - return true; - } - - return false; -} - -Value FunctionObject::construct(Managed *that, Value *, int) -{ - FunctionObject *f = static_cast(that); - ExecutionEngine *v4 = f->engine(); - - Object *obj = v4->newObject(); - Value proto = f->get(v4->id_prototype); - if (proto.isObject()) - obj->prototype = proto.objectValue(); - return Value::fromObject(obj); -} - -Value FunctionObject::call(Managed *, const Value &, Value *, int) -{ - return Value::undefinedValue(); -} - -void FunctionObject::markObjects(Managed *that) -{ - FunctionObject *o = static_cast(that); - if (o->name) - o->name->mark(); - // these are marked in VM::Function: -// for (uint i = 0; i < formalParameterCount; ++i) -// formalParameterList[i]->mark(); -// for (uint i = 0; i < varCount; ++i) -// varList[i]->mark(); - o->scope->mark(); - if (o->function) - o->function->mark(); - - Object::markObjects(that); -} - - -DEFINE_MANAGED_VTABLE(FunctionCtor); - -FunctionCtor::FunctionCtor(ExecutionContext *scope) - : FunctionObject(scope, scope->engine->newIdentifier(QStringLiteral("Function"))) -{ - vtbl = &static_vtbl; -} - -// 15.3.2 -Value FunctionCtor::construct(Managed *that, Value *args, int argc) -{ - FunctionCtor *f = static_cast(that); - MemoryManager::GCBlocker gcBlocker(f->engine()->memoryManager); - - ExecutionContext *ctx = f->engine()->current; - QString arguments; - QString body; - if (argc > 0) { - for (uint i = 0; i < argc - 1; ++i) { - if (i) - arguments += QLatin1String(", "); - arguments += args[i].toString(ctx)->toQString(); - } - body = args[argc - 1].toString(ctx)->toQString(); - } - - QString function = QLatin1String("function(") + arguments + QLatin1String("){") + body + QLatin1String("}"); - - QQmlJS::Engine ee, *engine = ⅇ - QQmlJS::Lexer lexer(engine); - lexer.setCode(function, 1, false); - QQmlJS::Parser parser(engine); - - const bool parsed = parser.parseExpression(); - - if (!parsed) - f->engine()->current->throwSyntaxError(0); - - using namespace QQmlJS::AST; - FunctionExpression *fe = QQmlJS::AST::cast(parser.rootNode()); - ExecutionEngine *v4 = f->engine(); - if (!fe) - v4->current->throwSyntaxError(0); - - QQmlJS::V4IR::Module module; - - QQmlJS::Codegen cg(v4->current, f->strictMode); - QQmlJS::V4IR::Function *irf = cg(QString(), function, fe, &module); - - QScopedPointer isel(v4->iselFactory->create(v4, &module)); - QV4::Function *vmf = isel->vmFunction(irf); - - return Value::fromObject(v4->newScriptFunction(v4->rootContext, vmf)); -} - -// 15.3.1: This is equivalent to new Function(...) -Value FunctionCtor::call(Managed *that, const Value &, Value *args, int argc) -{ - return construct(that, args, argc); -} - -FunctionPrototype::FunctionPrototype(ExecutionContext *ctx) - : FunctionObject(ctx) -{ -} - -void FunctionPrototype::init(ExecutionContext *ctx, const Value &ctor) -{ - ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); - ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); - - defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(0)); - defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); - defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); - defineDefaultProperty(ctx, QStringLiteral("apply"), method_apply, 2); - defineDefaultProperty(ctx, QStringLiteral("call"), method_call, 1); - defineDefaultProperty(ctx, QStringLiteral("bind"), method_bind, 1); - -} - -Value FunctionPrototype::method_toString(SimpleCallContext *ctx) -{ - FunctionObject *fun = ctx->thisObject.asFunctionObject(); - if (!fun) - ctx->throwTypeError(); - - return Value::fromString(ctx, QStringLiteral("function() { [code] }")); -} - -Value FunctionPrototype::method_apply(SimpleCallContext *ctx) -{ - Value thisArg = ctx->argument(0); - - Value arg = ctx->argument(1); - QVector args; - - if (Object *arr = arg.asObject()) { - quint32 len = arr->get(ctx->engine->id_length).toUInt32(); - for (quint32 i = 0; i < len; ++i) { - Value a = arr->getIndexed(i); - args.append(a); - } - } else if (!(arg.isUndefined() || arg.isNull())) { - ctx->throwTypeError(); - return Value::undefinedValue(); - } - - FunctionObject *o = ctx->thisObject.asFunctionObject(); - if (!o) - ctx->throwTypeError(); - - return o->call(thisArg, args.data(), args.size()); -} - -Value FunctionPrototype::method_call(SimpleCallContext *ctx) -{ - Value thisArg = ctx->argument(0); - - QVector args(ctx->argumentCount ? ctx->argumentCount - 1 : 0); - if (ctx->argumentCount) - qCopy(ctx->arguments + 1, - ctx->arguments + ctx->argumentCount, args.begin()); - - FunctionObject *o = ctx->thisObject.asFunctionObject(); - if (!o) - ctx->throwTypeError(); - - return o->call(thisArg, args.data(), args.size()); -} - -Value FunctionPrototype::method_bind(SimpleCallContext *ctx) -{ - FunctionObject *target = ctx->thisObject.asFunctionObject(); - if (!target) - ctx->throwTypeError(); - - Value boundThis = ctx->argument(0); - QVector boundArgs; - for (uint i = 1; i < ctx->argumentCount; ++i) - boundArgs += ctx->argument(i); - - - BoundFunction *f = ctx->engine->newBoundFunction(ctx->engine->rootContext, target, boundThis, boundArgs); - return Value::fromObject(f); -} - - -static Value throwTypeError(SimpleCallContext *ctx) -{ - ctx->throwTypeError(); - return Value::undefinedValue(); -} - -DEFINE_MANAGED_VTABLE(ScriptFunction); - -ScriptFunction::ScriptFunction(ExecutionContext *scope, Function *function) - : FunctionObject(scope, function->name) -{ - vtbl = &static_vtbl; - this->function = function; - assert(function); - assert(function->code); - - // global function - if (!scope) - return; - - MemoryManager::GCBlocker gcBlocker(scope->engine->memoryManager); - - needsActivation = function->needsActivation(); - usesArgumentsObject = function->usesArgumentsObject; - strictMode = function->isStrict; - formalParameterCount = function->formals.size(); - formalParameterList = function->formals.constData(); - defineReadonlyProperty(scope->engine->id_length, Value::fromInt32(formalParameterCount)); - - varCount = function->locals.size(); - varList = function->locals.constData(); - - Object *proto = scope->engine->newObject(); - proto->defineDefaultProperty(scope->engine->id_constructor, Value::fromObject(this)); - Property *pd = insertMember(scope->engine->id_prototype, Attr_NotEnumerable|Attr_NotConfigurable); - pd->value = Value::fromObject(proto); - - if (scope->strictMode) { - FunctionObject *thrower = scope->engine->newBuiltinFunction(scope, 0, throwTypeError); - Property pd = Property::fromAccessor(thrower, thrower); - __defineOwnProperty__(scope, QStringLiteral("caller"), pd, Attr_Accessor|Attr_NotEnumerable|Attr_NotConfigurable); - __defineOwnProperty__(scope, QStringLiteral("arguments"), pd, Attr_Accessor|Attr_NotEnumerable|Attr_NotConfigurable); - } -} - -Value ScriptFunction::construct(Managed *that, Value *args, int argc) -{ - ScriptFunction *f = static_cast(that); - assert(f->function->code); - ExecutionEngine *v4 = f->engine(); - Object *obj = v4->newObject(); - Value proto = f->get(v4->id_prototype); - if (proto.isObject()) - obj->prototype = proto.objectValue(); - - ExecutionContext *context = v4->current; - quintptr stackSpace[stackContextSize/sizeof(quintptr)]; - ExecutionContext *ctx = v4->newCallContext(stackSpace, f, Value::fromObject(obj), args, argc); - - Value result; - try { - result = f->function->code(ctx, f->function->codeData); - } catch (Exception &ex) { - ex.partiallyUnwindContext(context); - throw; - } - ctx->engine->popContext(); - - if (result.isObject()) - return result; - return Value::fromObject(obj); -} - -Value ScriptFunction::call(Managed *that, const Value &thisObject, Value *args, int argc) -{ - ScriptFunction *f = static_cast(that); - assert(f->function->code); - quintptr stackSpace[stackContextSize/sizeof(quintptr)]; - ExecutionContext *context = f->engine()->current; - ExecutionContext *ctx = f->engine()->newCallContext(stackSpace, f, thisObject, args, argc); - - if (!f->strictMode && !thisObject.isObject()) { - if (thisObject.isUndefined() || thisObject.isNull()) { - ctx->thisObject = Value::fromObject(f->engine()->globalObject); - } else { - ctx->thisObject = Value::fromObject(thisObject.toObject(context)); - } - } - - Value result; - try { - result = f->function->code(ctx, f->function->codeData); - } catch (Exception &ex) { - ex.partiallyUnwindContext(context); - throw; - } - ctx->engine->popContext(); - return result; -} - - - -DEFINE_MANAGED_VTABLE(BuiltinFunctionOld); - -BuiltinFunctionOld::BuiltinFunctionOld(ExecutionContext *scope, String *name, Value (*code)(SimpleCallContext *)) - : FunctionObject(scope, name) - , code(code) -{ - vtbl = &static_vtbl; - isBuiltinFunction = true; -} - -Value BuiltinFunctionOld::construct(Managed *f, Value *, int) -{ - f->engine()->current->throwTypeError(); - return Value::undefinedValue(); -} - -Value BuiltinFunctionOld::call(Managed *that, const Value &thisObject, Value *args, int argc) -{ - BuiltinFunctionOld *f = static_cast(that); - ExecutionEngine *v4 = f->engine(); - ExecutionContext *context = v4->current; - - SimpleCallContext ctx; - ctx.initSimpleCallContext(f->scope->engine); - ctx.strictMode = f->scope->strictMode; // ### needed? scope or parent context? - ctx.thisObject = thisObject; - ctx.arguments = args; - ctx.argumentCount = argc; - v4->pushContext(&ctx); - - if (!f->strictMode && !thisObject.isObject()) { - // Built-in functions allow for the this object to be null or undefined. This overrides - // the behaviour of changing thisObject to the global object if null/undefined and allows - // the built-in functions for example to throw a type error if null is passed. - if (!thisObject.isUndefined() && !thisObject.isNull()) - ctx.thisObject = Value::fromObject(thisObject.toObject(context)); - } - - Value result = Value::undefinedValue(); - try { - result = f->code(&ctx); - } catch (Exception &ex) { - ex.partiallyUnwindContext(context); - throw; - } - - context->engine->popContext(); - return result; -} - -Value IndexedBuiltinFunction::call(Managed *that, const Value &thisObject, Value *args, int argc) -{ - IndexedBuiltinFunction *f = static_cast(that); - ExecutionEngine *v4 = f->engine(); - ExecutionContext *context = v4->current; - - SimpleCallContext ctx; - ctx.initSimpleCallContext(f->scope->engine); - ctx.strictMode = f->scope->strictMode; // ### needed? scope or parent context? - ctx.thisObject = thisObject; - ctx.arguments = args; - ctx.argumentCount = argc; - v4->pushContext(&ctx); - - if (!f->strictMode && !thisObject.isObject()) { - // Built-in functions allow for the this object to be null or undefined. This overrides - // the behaviour of changing thisObject to the global object if null/undefined and allows - // the built-in functions for example to throw a type error if null is passed. - if (!thisObject.isUndefined() && !thisObject.isNull()) - ctx.thisObject = Value::fromObject(thisObject.toObject(context)); - } - - Value result = Value::undefinedValue(); - try { - result = f->code(&ctx, f->index); - } catch (Exception &ex) { - ex.partiallyUnwindContext(context); - throw; - } - - context->engine->popContext(); - return result; -} - -DEFINE_MANAGED_VTABLE(IndexedBuiltinFunction); - -DEFINE_MANAGED_VTABLE(BoundFunction); - -BoundFunction::BoundFunction(ExecutionContext *scope, FunctionObject *target, Value boundThis, const QVector &boundArgs) - : FunctionObject(scope, 0) - , target(target) - , boundThis(boundThis) - , boundArgs(boundArgs) -{ - vtbl = &static_vtbl; - int len = target->get(scope->engine->id_length).toUInt32(); - len -= boundArgs.size(); - if (len < 0) - len = 0; - defineReadonlyProperty(scope->engine->id_length, Value::fromInt32(len)); - - FunctionObject *thrower = scope->engine->newBuiltinFunction(scope, 0, throwTypeError); - Property pd = Property::fromAccessor(thrower, thrower); - *insertMember(scope->engine->id_arguments, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable) = pd; - *insertMember(scope->engine->id_caller, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable) = pd; -} - -void BoundFunction::destroy(Managed *that) -{ - static_cast(that)->~BoundFunction(); -} - -Value BoundFunction::call(Managed *that, const Value &, Value *args, int argc) -{ - BoundFunction *f = static_cast(that); - Value *newArgs = static_cast(alloca(sizeof(Value)*(f->boundArgs.size() + argc))); - memcpy(newArgs, f->boundArgs.constData(), f->boundArgs.size()*sizeof(Value)); - memcpy(newArgs + f->boundArgs.size(), args, argc*sizeof(Value)); - - return f->target->call(f->boundThis, newArgs, f->boundArgs.size() + argc); -} - -Value BoundFunction::construct(Managed *that, Value *args, int argc) -{ - BoundFunction *f = static_cast(that); - Value *newArgs = static_cast(alloca(sizeof(Value)*(f->boundArgs.size() + argc))); - memcpy(newArgs, f->boundArgs.constData(), f->boundArgs.size()*sizeof(Value)); - memcpy(newArgs + f->boundArgs.size(), args, argc*sizeof(Value)); - - return f->target->construct(newArgs, f->boundArgs.size() + argc); -} - -bool BoundFunction::hasInstance(Managed *that, const Value &value) -{ - BoundFunction *f = static_cast(that); - return FunctionObject::hasInstance(f->target, value); -} - -void BoundFunction::markObjects(Managed *that) -{ - BoundFunction *o = static_cast(that); - o->target->mark(); - if (Managed *m = o->boundThis.asManaged()) - m->mark(); - for (int i = 0; i < o->boundArgs.size(); ++i) - if (Managed *m = o->boundArgs.at(i).asManaged()) - m->mark(); - FunctionObject::markObjects(that); -} diff --git a/src/qml/qml/v4/qv4functionobject_p.h b/src/qml/qml/v4/qv4functionobject_p.h deleted file mode 100644 index 39e07a2b8d..0000000000 --- a/src/qml/qml/v4/qv4functionobject_p.h +++ /dev/null @@ -1,223 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QV4FUNCTIONOBJECT_H -#define QV4FUNCTIONOBJECT_H - -#include "qv4global_p.h" -#include "qv4runtime_p.h" -#include "qv4engine_p.h" -#include "qv4context_p.h" -#include "qv4object_p.h" -#include "qv4string_p.h" -#include "qv4codegen_p.h" -#include "qv4isel_p.h" -#include "qv4managed_p.h" -#include "qv4property_p.h" -#include "qv4objectiterator_p.h" - -#include -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE - -namespace QV4 { - -struct Function; -struct Object; -struct BooleanObject; -struct NumberObject; -struct StringObject; -struct ArrayObject; -struct DateObject; -struct FunctionObject; -struct ErrorObject; -struct ArgumentsObject; -struct ExecutionContext; -struct ExecutionEngine; -class MemoryManager; - -struct ObjectPrototype; -struct StringPrototype; -struct NumberPrototype; -struct BooleanPrototype; -struct ArrayPrototype; -struct FunctionPrototype; -struct DatePrototype; -struct ErrorPrototype; -struct EvalErrorPrototype; -struct RangeErrorPrototype; -struct ReferenceErrorPrototype; -struct SyntaxErrorPrototype; -struct TypeErrorPrototype; -struct URIErrorPrototype; -struct InternalClass; -struct Lookup; - -struct Q_QML_EXPORT FunctionObject: Object { - // Used with Managed::subType - enum FunctionType { - RegularFunction = 0, - WrappedQtMethod = 1 - }; - - ExecutionContext *scope; - String *name; - String * const *formalParameterList; - String * const *varList; - unsigned int formalParameterCount; - unsigned int varCount; - Function *function; - - FunctionObject(ExecutionContext *scope, String *name = 0); - - Value newInstance(); - - static Value construct(Managed *that, Value *args, int argc); - static Value call(Managed *that, const Value &, Value *, int); - inline Value construct(Value *args, int argc) { - return vtbl->construct(this, args, argc); - } - inline Value call(const Value &thisObject, Value *args, int argc) { - return vtbl->call(this, thisObject, args, argc); - } - -protected: - static const ManagedVTable static_vtbl; - static void markObjects(Managed *that); - static bool hasInstance(Managed *that, const Value &value); -}; - -struct FunctionCtor: FunctionObject -{ - FunctionCtor(ExecutionContext *scope); - - static Value construct(Managed *that, Value *args, int argc); - static Value call(Managed *that, const Value &, Value *, int); - -protected: - static const ManagedVTable static_vtbl; -}; - -struct FunctionPrototype: FunctionObject -{ - FunctionPrototype(ExecutionContext *ctx); - void init(ExecutionContext *ctx, const Value &ctor); - - static Value method_toString(SimpleCallContext *ctx); - static Value method_apply(SimpleCallContext *ctx); - static Value method_call(SimpleCallContext *ctx); - static Value method_bind(SimpleCallContext *ctx); -}; - -struct BuiltinFunctionOld: FunctionObject { - Value (*code)(SimpleCallContext *); - - BuiltinFunctionOld(ExecutionContext *scope, String *name, Value (*code)(SimpleCallContext *)); - - static Value construct(Managed *, Value *args, int argc); - static Value call(Managed *that, const Value &, Value *, int); - -protected: - static const ManagedVTable static_vtbl; -}; - -struct IndexedBuiltinFunction: FunctionObject -{ - Q_MANAGED - - Value (*code)(SimpleCallContext *ctx, uint index); - uint index; - - IndexedBuiltinFunction(ExecutionContext *scope, uint index, Value (*code)(SimpleCallContext *ctx, uint index)) - : FunctionObject(scope, /*name*/0) - , code(code) - , index(index) - { - vtbl = &static_vtbl; - isBuiltinFunction = true; - } - - static Value construct(Managed *m, Value *, int) - { - m->engine()->current->throwTypeError(); - return Value::undefinedValue(); - } - - static Value call(Managed *that, const Value &thisObject, Value *args, int argc); -}; - - -struct ScriptFunction: FunctionObject { - ScriptFunction(ExecutionContext *scope, Function *function); - - static Value construct(Managed *, Value *args, int argc); - static Value call(Managed *that, const Value &, Value *, int); - -protected: - static const ManagedVTable static_vtbl; -}; - -struct BoundFunction: FunctionObject { - FunctionObject *target; - Value boundThis; - QVector boundArgs; - - BoundFunction(ExecutionContext *scope, FunctionObject *target, Value boundThis, const QVector &boundArgs); - ~BoundFunction() {} - - - static Value construct(Managed *, Value *args, int argc); - static Value call(Managed *that, const Value &, Value *, int); - - static const ManagedVTable static_vtbl; - static void destroy(Managed *); - static void markObjects(Managed *that); - static bool hasInstance(Managed *that, const Value &value); -}; - -} - -QT_END_NAMESPACE - -#endif // QMLJS_OBJECTS_H diff --git a/src/qml/qml/v4/qv4global_p.h b/src/qml/qml/v4/qv4global_p.h deleted file mode 100644 index 8e47f3c88a..0000000000 --- a/src/qml/qml/v4/qv4global_p.h +++ /dev/null @@ -1,197 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QV4GLOBAL_H -#define QV4GLOBAL_H - -#include - -#include - -#if defined(Q_CC_MSVC) -namespace std { - -inline bool isinf(double d) { return !_finite(d) && !_isnan(d); } -inline bool isnan(double d) { return !!_isnan(d); } -inline bool isfinite(double d) { return _finite(d); } -inline bool signbit(double d) { return _copysign(1.0, d) < 0; } - -} // namespace std - -inline double trunc(double d) { return d > 0 ? floor(d) : ceil(d); } -#endif - -QT_BEGIN_NAMESPACE - -namespace QV4 { - -class MemoryManager; -struct String; -struct Object; -struct ObjectPrototype; -struct ObjectIterator; -struct ExecutionContext; -struct ScriptFunction; -struct InternalClass; -struct Property; - -struct BooleanObject; -struct NumberObject; -struct StringObject; -struct ArrayObject; -struct DateObject; -struct FunctionObject; -struct ErrorObject; -struct ArgumentsObject; -struct Managed; -struct Lookup; -struct ExecutionEngine; -struct QObjectWrapper; - - -enum PropertyFlag { - Attr_Data = 0, - Attr_Accessor = 0x1, - Attr_NotWritable = 0x2, - Attr_NotEnumerable = 0x4, - Attr_NotConfigurable = 0x8, - Attr_ReadOnly = Attr_NotWritable|Attr_NotEnumerable|Attr_NotConfigurable, - Attr_Invalid = 0xff -}; - -Q_DECLARE_FLAGS(PropertyFlags, PropertyFlag); -Q_DECLARE_OPERATORS_FOR_FLAGS(PropertyFlags); - -struct PropertyAttributes -{ - union { - uchar m_all; - struct { - uchar m_flags : 4; - uchar m_mask : 4; - }; - struct { - uchar m_type : 1; - uchar m_writable : 1; - uchar m_enumerable : 1; - uchar m_configurable : 1; - uchar type_set : 1; - uchar writable_set : 1; - uchar enumerable_set : 1; - uchar configurable_set : 1; - }; - }; - - enum Type { - Data = 0, - Accessor = 1, - Generic = 2 - }; - - PropertyAttributes() : m_all(0) {} - PropertyAttributes(PropertyFlag f) : m_all(0) { - if (f != Attr_Invalid) { - setType(f & Attr_Accessor ? Accessor : Data); - if (!(f & Attr_Accessor)) - setWritable(!(f & Attr_NotWritable)); - setEnumerable(!(f & Attr_NotEnumerable)); - setConfigurable(!(f & Attr_NotConfigurable)); - } - } - PropertyAttributes(PropertyFlags f) : m_all(0) { - if (f != Attr_Invalid) { - setType(f & Attr_Accessor ? Accessor : Data); - if (!(f & Attr_Accessor)) - setWritable(!(f & Attr_NotWritable)); - setEnumerable(!(f & Attr_NotEnumerable)); - setConfigurable(!(f & Attr_NotConfigurable)); - } - } - PropertyAttributes(const PropertyAttributes &other) : m_all(other.m_all) {} - PropertyAttributes & operator=(const PropertyAttributes &other) { m_all = other.m_all; return *this; } - - void setType(Type t) { m_type = t; type_set = true; } - Type type() const { return type_set ? (Type)m_type : Generic; } - - bool isData() const { return type() == PropertyAttributes::Data || writable_set; } - bool isAccessor() const { return type() == PropertyAttributes::Accessor; } - bool isGeneric() const { return type() == PropertyAttributes::Generic && !writable_set; } - - bool hasType() const { return type_set; } - bool hasWritable() const { return writable_set; } - bool hasConfigurable() const { return configurable_set; } - bool hasEnumerable() const { return enumerable_set; } - - void setWritable(bool b) { m_writable = b; writable_set = true; } - void setConfigurable(bool b) { m_configurable = b; configurable_set = true; } - void setEnumerable(bool b) { m_enumerable = b; enumerable_set = true; } - - void resolve() { m_mask = 0xf; if (m_type == Accessor) { m_writable = false; writable_set = false; } } - - bool isWritable() const { return m_type != Data || m_writable; } - bool isEnumerable() const { return m_enumerable; } - bool isConfigurable() const { return m_configurable; } - - void clearType() { m_type = Data; type_set = false; } - void clearWritable() { m_writable = false; writable_set = false; } - void clearEnumerable() { m_enumerable = false; enumerable_set = false; } - void clearConfigurable() { m_configurable = false; configurable_set = false; } - - void clear() { m_all = 0; } - bool isEmpty() const { return !m_all; } - - uint flags() const { return m_flags; } - - bool operator==(PropertyAttributes other) { - return m_all == other.m_all; - } - bool operator!=(PropertyAttributes other) { - return m_all != other.m_all; - } -}; - -} - -Q_DECLARE_TYPEINFO(QV4::PropertyAttributes, Q_PRIMITIVE_TYPE); - -QT_END_NAMESPACE - -#endif // QV4GLOBAL_H diff --git a/src/qml/qml/v4/qv4globalobject.cpp b/src/qml/qml/v4/qv4globalobject.cpp deleted file mode 100644 index 6b279416a3..0000000000 --- a/src/qml/qml/v4/qv4globalobject.cpp +++ /dev/null @@ -1,652 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4globalobject_p.h" -#include "qv4mm_p.h" -#include "qv4value_p.h" -#include "qv4context_p.h" -#include "qv4function_p.h" -#include "qv4debugging_p.h" -#include "qv4script_p.h" -#include "qv4exception_p.h" - -#include -#include -#include -#include -#include -#include -#include "private/qlocale_tools_p.h" - -#include -#include -#include -#include "qv4alloca_p.h" - -#include - -using namespace QV4; - - -static inline char toHex(char c) -{ - static const char hexnumbers[] = "0123456789ABCDEF"; - return hexnumbers[c & 0xf]; -} - -static int fromHex(QChar ch) -{ - ushort c = ch.unicode(); - if ((c >= '0') && (c <= '9')) - return c - '0'; - if ((c >= 'A') && (c <= 'F')) - return c - 'A' + 10; - if ((c >= 'a') && (c <= 'f')) - return c - 'a' + 10; - return -1; -} - -static QString escape(const QString &input) -{ - QString output; - output.reserve(input.size() * 3); - const int length = input.length(); - for (int i = 0; i < length; ++i) { - ushort uc = input.at(i).unicode(); - if (uc < 0x100) { - if ( (uc > 0x60 && uc < 0x7B) - || (uc > 0x3F && uc < 0x5B) - || (uc > 0x2C && uc < 0x3A) - || (uc == 0x2A) - || (uc == 0x2B) - || (uc == 0x5F)) { - output.append(QChar(uc)); - } else { - output.append('%'); - output.append(QChar(toHex(uc >> 4))); - output.append(QChar(toHex(uc))); - } - } else { - output.append('%'); - output.append('u'); - output.append(QChar(toHex(uc >> 12))); - output.append(QChar(toHex(uc >> 8))); - output.append(QChar(toHex(uc >> 4))); - output.append(QChar(toHex(uc))); - } - } - return output; -} - -static QString unescape(const QString &input) -{ - QString result; - result.reserve(input.length()); - int i = 0; - const int length = input.length(); - while (i < length) { - QChar c = input.at(i++); - if ((c == '%') && (i + 1 < length)) { - QChar a = input.at(i); - if ((a == 'u') && (i + 4 < length)) { - int d3 = fromHex(input.at(i+1)); - int d2 = fromHex(input.at(i+2)); - int d1 = fromHex(input.at(i+3)); - int d0 = fromHex(input.at(i+4)); - if ((d3 != -1) && (d2 != -1) && (d1 != -1) && (d0 != -1)) { - ushort uc = ushort((d3 << 12) | (d2 << 8) | (d1 << 4) | d0); - result.append(QChar(uc)); - i += 5; - } else { - result.append(c); - } - } else { - int d1 = fromHex(a); - int d0 = fromHex(input.at(i+1)); - if ((d1 != -1) && (d0 != -1)) { - c = (d1 << 4) | d0; - i += 2; - } - result.append(c); - } - } else { - result.append(c); - } - } - return result; -} - -static const char uriReserved[] = ";/?:@&=+$,#"; -static const char uriUnescaped[] = "-_.!~*'()"; -static const char uriUnescapedReserved[] = "-_.!~*'();/?:@&=+$,#"; - -static void addEscapeSequence(QString &output, uchar ch) -{ - output.append(QLatin1Char('%')); - output.append(QLatin1Char(toHex(ch >> 4))); - output.append(QLatin1Char(toHex(ch & 0xf))); -} - -static QString encode(const QString &input, const char *unescapedSet, bool *ok) -{ - *ok = true; - QString output; - const int length = input.length(); - int i = 0; - while (i < length) { - const QChar c = input.at(i); - bool escape = true; - if ((c.unicode() >= 'a' && c.unicode() <= 'z') || - (c.unicode() >= 'A' && c.unicode() <= 'Z') || - (c.unicode() >= '0' && c.unicode() <= '9')) { - escape = false; - } else { - const char *r = unescapedSet; - while (*r) { - if (*r == c.unicode()) { - escape = false; - break; - } - ++r; - } - } - if (escape) { - uint uc = c.unicode(); - if ((uc >= 0xDC00) && (uc <= 0xDFFF)) { - *ok = false; - break; - } - if (!((uc < 0xD800) || (uc > 0xDBFF))) { - ++i; - if (i == length) { - *ok = false; - break; - } - const uint uc2 = input.at(i).unicode(); - if ((uc2 < 0xDC00) || (uc2 > 0xDFFF)) { - *ok = false; - break; - } - uc = ((uc - 0xD800) * 0x400) + (uc2 - 0xDC00) + 0x10000; - } - if (uc < 0x80) { - addEscapeSequence(output, (uchar)uc); - } else { - if (uc < 0x0800) { - addEscapeSequence(output, 0xc0 | ((uchar) (uc >> 6))); - } else { - - if (QChar::requiresSurrogates(uc)) { - addEscapeSequence(output, 0xf0 | ((uchar) (uc >> 18))); - addEscapeSequence(output, 0x80 | (((uchar) (uc >> 12)) & 0x3f)); - } else { - addEscapeSequence(output, 0xe0 | (((uchar) (uc >> 12)) & 0x3f)); - } - addEscapeSequence(output, 0x80 | (((uchar) (uc >> 6)) & 0x3f)); - } - addEscapeSequence(output, 0x80 | ((uchar) (uc&0x3f))); - } - } else { - output.append(c); - } - ++i; - } - if (i != length) - *ok = false; - return output; -} - -enum DecodeMode { - DecodeAll, - DecodeNonReserved -}; - -static QString decode(const QString &input, DecodeMode decodeMode, bool *ok) -{ - *ok = true; - QString output; - output.reserve(input.length()); - const int length = input.length(); - int i = 0; - const QChar percent = QLatin1Char('%'); - while (i < length) { - const QChar ch = input.at(i); - if (ch == percent) { - int start = i; - if (i + 2 >= length) - goto error; - - int d1 = fromHex(input.at(i+1)); - int d0 = fromHex(input.at(i+2)); - if ((d1 == -1) || (d0 == -1)) - goto error; - - int b = (d1 << 4) | d0; - i += 2; - if (b & 0x80) { - int uc; - int min_uc; - int need; - if ((b & 0xe0) == 0xc0) { - uc = b & 0x1f; - need = 1; - min_uc = 0x80; - } else if ((b & 0xf0) == 0xe0) { - uc = b & 0x0f; - need = 2; - min_uc = 0x800; - } else if ((b & 0xf8) == 0xf0) { - uc = b & 0x07; - need = 3; - min_uc = 0x10000; - } else { - goto error; - } - - if (i + (3 * need) >= length) - goto error; - - for (int j = 0; j < need; ++j) { - ++i; - if (input.at(i) != percent) - goto error; - - d1 = fromHex(input.at(i+1)); - d0 = fromHex(input.at(i+2)); - if ((d1 == -1) || (d0 == -1)) - goto error; - - b = (d1 << 4) | d0; - if ((b & 0xC0) != 0x80) - goto error; - - i += 2; - uc = (uc << 6) + (b & 0x3f); - } - if (uc < min_uc) - goto error; - - if (uc < 0x10000) { - output.append(QChar(uc)); - } else { - if (uc > 0x10FFFF) - goto error; - - ushort l = ushort(((uc - 0x10000) & 0x3FF) + 0xDC00); - ushort h = ushort((((uc - 0x10000) >> 10) & 0x3FF) + 0xD800); - output.append(QChar(h)); - output.append(QChar(l)); - } - } else { - if (decodeMode == DecodeNonReserved && b <= 0x40) { - const char *r = uriReserved; - while (*r) { - if (*r == b) - break; - ++r; - } - if (*r) - output.append(input.mid(start, i - start + 1)); - else - output.append(QChar(b)); - } else { - output.append(QChar(b)); - } - } - } else { - output.append(ch); - } - ++i; - } - if (i != length) - *ok = false; - return output; - error: - *ok = false; - return QString(); -} - -DEFINE_MANAGED_VTABLE(EvalFunction); - -EvalFunction::EvalFunction(ExecutionContext *scope) - : FunctionObject(scope, scope->engine->id_eval) -{ - vtbl = &static_vtbl; - defineReadonlyProperty(scope->engine->id_length, Value::fromInt32(1)); -} - -Value EvalFunction::evalCall(Value /*thisObject*/, Value *args, int argc, bool directCall) -{ - if (argc < 1) - return Value::undefinedValue(); - - ExecutionContext *parentContext = engine()->current; - ExecutionEngine *engine = parentContext->engine; - ExecutionContext *ctx = parentContext; - - if (!directCall) { - // the context for eval should be the global scope, so we fake a root - // context - ctx = engine->pushGlobalContext(); - } - - if (!args[0].isString()) - return args[0]; - - const QString code = args[0].stringValue()->toQString(); - bool inheritContext = !ctx->strictMode; - - Script script(ctx, code, QString("eval code")); - script.strictMode = (directCall && parentContext->strictMode); - script.inheritContext = inheritContext; - script.parse(); - - Function *function = script.function(); - if (!function) - return Value::undefinedValue(); - - strictMode = function->isStrict || (ctx->strictMode); - - usesArgumentsObject = function->usesArgumentsObject; - needsActivation = function->needsActivation(); - - if (strictMode) { - FunctionObject *e = engine->newScriptFunction(ctx, function); - return e->call(ctx->thisObject, 0, 0); - } - - ExecutionContext::EvalCode evalCode; - evalCode.function = function; - evalCode.next = ctx->currentEvalCode; - ctx->currentEvalCode = &evalCode; - - // set the correct strict mode flag on the context - bool cstrict = ctx->strictMode; - ctx->strictMode = strictMode; - - Value result = Value::undefinedValue(); - try { - result = function->code(ctx, function->codeData); - } catch (Exception &ex) { - ctx->strictMode = cstrict; - ctx->currentEvalCode = evalCode.next; - if (strictMode) - ex.partiallyUnwindContext(parentContext); - throw; - } - - ctx->strictMode = cstrict; - ctx->currentEvalCode = evalCode.next; - - while (engine->current != parentContext) - engine->popContext(); - - return result; -} - - -Value EvalFunction::call(Managed *that, const Value &thisObject, Value *args, int argc) -{ - // indirect call - return static_cast(that)->evalCall(thisObject, args, argc, false); -} - - -static inline int toInt(const QChar &qc, int R) -{ - ushort c = qc.unicode(); - int v = -1; - if (c >= '0' && c <= '9') - v = c - '0'; - else if (c >= 'A' && c <= 'Z') - v = c - 'A' + 10; - else if (c >= 'a' && c <= 'z') - v = c - 'a' + 10; - if (v >= 0 && v < R) - return v; - else - return -1; -} - -// parseInt [15.1.2.2] -Value GlobalFunctions::method_parseInt(SimpleCallContext *context) -{ - Value string = context->argument(0); - Value radix = context->argument(1); - int R = radix.isUndefined() ? 0 : radix.toInt32(); - - // [15.1.2.2] step by step: - String *inputString = string.toString(context); // 1 - QString trimmed = inputString->toQString().trimmed(); // 2 - const QChar *pos = trimmed.constData(); - const QChar *end = pos + trimmed.length(); - - int sign = 1; // 3 - if (pos != end) { - if (*pos == QLatin1Char('-')) - sign = -1; // 4 - if (*pos == QLatin1Char('-') || *pos == QLatin1Char('+')) - ++pos; // 5 - } - bool stripPrefix = true; // 7 - if (R) { // 8 - if (R < 2 || R > 36) - return Value::fromDouble(std::numeric_limits::quiet_NaN()); // 8a - if (R != 16) - stripPrefix = false; // 8b - } else { // 9 - R = 10; // 9a - } - if (stripPrefix) { // 10 - if ((end - pos >= 2) - && (pos[0] == QLatin1Char('0')) - && (pos[1] == QLatin1Char('x') || pos[1] == QLatin1Char('X'))) { // 10a - pos += 2; - R = 16; - } - } - // 11: Z is progressively built below - // 13: this is handled by the toInt function - if (pos == end) // 12 - return Value::fromDouble(std::numeric_limits::quiet_NaN()); - bool overflow = false; - qint64 v_overflow; - unsigned overflow_digit_count = 0; - int d = toInt(*pos++, R); - if (d == -1) - return Value::fromDouble(std::numeric_limits::quiet_NaN()); - qint64 v = d; - while (pos != end) { - d = toInt(*pos++, R); - if (d == -1) - break; - if (overflow) { - if (overflow_digit_count == 0) { - v_overflow = v; - v = 0; - } - ++overflow_digit_count; - v = v * R + d; - } else { - qint64 vNew = v * R + d; - if (vNew < v) { - overflow = true; - --pos; - } else { - v = vNew; - } - } - } - - if (overflow) { - double result = (double) v_overflow * pow(R, overflow_digit_count); - result += v; - return Value::fromDouble(sign * result); - } else { - return Value::fromDouble(sign * (double) v); // 15 - } -} - -// parseFloat [15.1.2.3] -Value GlobalFunctions::method_parseFloat(SimpleCallContext *context) -{ - Value string = context->argument(0); - - // [15.1.2.3] step by step: - String *inputString = string.toString(context); // 1 - QString trimmed = inputString->toQString().trimmed(); // 2 - - // 4: - if (trimmed.startsWith(QLatin1String("Infinity")) - || trimmed.startsWith(QLatin1String("+Infinity"))) - return Value::fromDouble(Q_INFINITY); - if (trimmed.startsWith("-Infinity")) - return Value::fromDouble(-Q_INFINITY); - QByteArray ba = trimmed.toLatin1(); - bool ok; - const char *begin = ba.constData(); - const char *end = 0; - double d = qstrtod(begin, &end, &ok); - if (end - begin == 0) - return Value::fromDouble(std::numeric_limits::quiet_NaN()); // 3 - else - return Value::fromDouble(d); -} - -/// isNaN [15.1.2.4] -Value GlobalFunctions::method_isNaN(SimpleCallContext *context) -{ - const Value &v = context->argument(0); - if (v.integerCompatible()) - return Value::fromBoolean(false); - - double d = v.toNumber(); - return Value::fromBoolean(std::isnan(d)); -} - -/// isFinite [15.1.2.5] -Value GlobalFunctions::method_isFinite(SimpleCallContext *context) -{ - const Value &v = context->argument(0); - if (v.integerCompatible()) - return Value::fromBoolean(true); - - double d = v.toNumber(); - return Value::fromBoolean(std::isfinite(d)); -} - -/// decodeURI [15.1.3.1] -Value GlobalFunctions::method_decodeURI(SimpleCallContext *context) -{ - if (context->argumentCount == 0) - return Value::undefinedValue(); - - QString uriString = context->arguments[0].toString(context)->toQString(); - bool ok; - QString out = decode(uriString, DecodeNonReserved, &ok); - if (!ok) - context->throwURIError(Value::fromString(context, QStringLiteral("malformed URI sequence"))); - - return Value::fromString(context, out); -} - -/// decodeURIComponent [15.1.3.2] -Value GlobalFunctions::method_decodeURIComponent(SimpleCallContext *context) -{ - if (context->argumentCount == 0) - return Value::undefinedValue(); - - QString uriString = context->arguments[0].toString(context)->toQString(); - bool ok; - QString out = decode(uriString, DecodeAll, &ok); - if (!ok) - context->throwURIError(Value::fromString(context, QStringLiteral("malformed URI sequence"))); - - return Value::fromString(context, out); -} - -/// encodeURI [15.1.3.3] -Value GlobalFunctions::method_encodeURI(SimpleCallContext *context) -{ - if (context->argumentCount == 0) - return Value::undefinedValue(); - - QString uriString = context->arguments[0].toString(context)->toQString(); - bool ok; - QString out = encode(uriString, uriUnescapedReserved, &ok); - if (!ok) - context->throwURIError(Value::fromString(context, QStringLiteral("malformed URI sequence"))); - - return Value::fromString(context, out); -} - -/// encodeURIComponent [15.1.3.4] -Value GlobalFunctions::method_encodeURIComponent(SimpleCallContext *context) -{ - if (context->argumentCount == 0) - return Value::undefinedValue(); - - QString uriString = context->arguments[0].toString(context)->toQString(); - bool ok; - QString out = encode(uriString, uriUnescaped, &ok); - if (!ok) - context->throwURIError(Value::fromString(context, QStringLiteral("malformed URI sequence"))); - - return Value::fromString(context, out); -} - -Value GlobalFunctions::method_escape(SimpleCallContext *context) -{ - if (!context->argumentCount) - return Value::fromString(context, QStringLiteral("undefined")); - - QString str = context->argument(0).toString(context)->toQString(); - return Value::fromString(context, escape(str)); -} - -Value GlobalFunctions::method_unescape(SimpleCallContext *context) -{ - if (!context->argumentCount) - return Value::fromString(context, QStringLiteral("undefined")); - - QString str = context->argument(0).toString(context)->toQString(); - return Value::fromString(context, unescape(str)); -} diff --git a/src/qml/qml/v4/qv4globalobject_p.h b/src/qml/qml/v4/qv4globalobject_p.h deleted file mode 100644 index 11d034b5b4..0000000000 --- a/src/qml/qml/v4/qv4globalobject_p.h +++ /dev/null @@ -1,82 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QV4GLOBALOBJECT_H -#define QV4GLOBALOBJECT_H - -#include "qv4global_p.h" -#include "qv4functionobject_p.h" - -QT_BEGIN_NAMESPACE - -namespace QV4 { - -struct Q_QML_EXPORT EvalFunction : FunctionObject -{ - EvalFunction(ExecutionContext *scope); - - Value evalCall(Value thisObject, Value *args, int argc, bool directCall); - - using Managed::construct; - static Value call(Managed *that, const Value &, Value *, int); - -protected: - static const ManagedVTable static_vtbl; -}; - -struct GlobalFunctions -{ - static Value method_parseInt(SimpleCallContext *context); - static Value method_parseFloat(SimpleCallContext *context); - static Value method_isNaN(SimpleCallContext *context); - static Value method_isFinite(SimpleCallContext *context); - static Value method_decodeURI(SimpleCallContext *context); - static Value method_decodeURIComponent(SimpleCallContext *context); - static Value method_encodeURI(SimpleCallContext *context); - static Value method_encodeURIComponent(SimpleCallContext *context); - static Value method_escape(SimpleCallContext *context); - static Value method_unescape(SimpleCallContext *context); -}; - -} - -QT_END_NAMESPACE - -#endif // QMLJS_OBJECTS_H diff --git a/src/qml/qml/v4/qv4identifier.cpp b/src/qml/qml/v4/qv4identifier.cpp deleted file mode 100644 index 5d8077bfdc..0000000000 --- a/src/qml/qml/v4/qv4identifier.cpp +++ /dev/null @@ -1,172 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#include "qv4identifier_p.h" -#include "qv4identifiertable_p.h" - -QT_BEGIN_NAMESPACE - -namespace QV4 { - -static const uchar prime_deltas[] = { - 0, 0, 1, 3, 1, 5, 3, 3, 1, 9, 7, 5, 3, 9, 25, 3, - 1, 21, 3, 21, 7, 15, 9, 5, 3, 29, 15, 0, 0, 0, 0, 0 -}; - -static inline int primeForNumBits(int numBits) -{ - return (1 << numBits) + prime_deltas[numBits]; -} - - -IdentifierHashData::IdentifierHashData(int numBits) - : numBits(numBits) - , size(0) -{ - refCount.store(1); - alloc = primeForNumBits(numBits); - entries = (IdentifierHashEntry *)malloc(alloc*sizeof(IdentifierHashEntry)); - memset(entries, 0, alloc*sizeof(IdentifierHashEntry)); -} - -IdentifierHashBase::IdentifierHashBase(ExecutionEngine *engine) -{ - d = new IdentifierHashData(3); - d->identifierTable = engine->identifierTable; -} - - -IdentifierHashEntry *IdentifierHashBase::addEntry(const Identifier *identifier) -{ - // fill up to max 50% - bool grow = (d->alloc <= d->size*2); - - if (grow) { - ++d->numBits; - int newAlloc = primeForNumBits(d->numBits); - IdentifierHashEntry *newEntries = (IdentifierHashEntry *)malloc(newAlloc * sizeof(IdentifierHashEntry)); - memset(newEntries, 0, newAlloc*sizeof(IdentifierHashEntry)); - for (uint i = 0; i < d->alloc; ++i) { - const IdentifierHashEntry &e = d->entries[i]; - if (!e.identifier) - continue; - uint idx = e.identifier->hashValue % newAlloc; - while (newEntries[idx].identifier) { - ++idx; - idx %= newAlloc; - } - newEntries[idx] = e; - } - free(d->entries); - d->entries = newEntries; - d->alloc = newAlloc; - } - - uint idx = identifier->hashValue % d->alloc; - while (d->entries[idx].identifier) { - Q_ASSERT(d->entries[idx].identifier != identifier); - ++idx; - idx %= d->alloc; - } - d->entries[idx].identifier = identifier; - ++d->size; - return d->entries + idx; -} - -const IdentifierHashEntry *IdentifierHashBase::lookup(const Identifier *identifier) const -{ - if (!d) - return 0; - assert(d->entries); - - uint idx = identifier->hashValue % d->alloc; - while (1) { - if (!d->entries[idx].identifier) - return 0; - if (d->entries[idx].identifier == identifier) - return d->entries + idx; - ++idx; - idx %= d->alloc; - } -} - -const IdentifierHashEntry *IdentifierHashBase::lookup(const QString &str) const -{ - if (!d) - return 0; - assert(d->entries); - - uint hash = String::createHashValue(str.constData(), str.length()); - uint idx = hash % d->alloc; - while (1) { - if (!d->entries[idx].identifier) - return 0; - if (d->entries[idx].identifier->string == str) - return d->entries + idx; - ++idx; - idx %= d->alloc; - } -} - -const IdentifierHashEntry *IdentifierHashBase::lookup(String *str) const -{ - if (!d) - return 0; - if (str->identifier) - return lookup(str->identifier); - return lookup(str->toQString()); -} - -const Identifier *IdentifierHashBase::toIdentifier(const QString &str) const -{ - Q_ASSERT(d); - return d->identifierTable->identifier(str); -} - -const Identifier *IdentifierHashBase::toIdentifier(String *str) const -{ - Q_ASSERT(d); - return d->identifierTable->identifier(str); -} - - -} - -QT_END_NAMESPACE diff --git a/src/qml/qml/v4/qv4identifier_p.h b/src/qml/qml/v4/qv4identifier_p.h deleted file mode 100644 index 7c69e1d8c4..0000000000 --- a/src/qml/qml/v4/qv4identifier_p.h +++ /dev/null @@ -1,220 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QV4IDENTIFIER_H -#define QV4IDENTIFIER_H - -#include - -QT_BEGIN_NAMESPACE - -namespace QV4 { - -struct String; -struct IdentifierTable; -struct ExecutionEngine; - -struct Identifier -{ - QString string; - uint hashValue; -}; - - -struct IdentifierHashEntry { - const Identifier *identifier; - union { - int value; - void *pointer; - }; - int get(int *) const { return this ? value : -1; } - bool get(bool *) const { return this != 0; } - void *get(void **) const { return this ? pointer : 0; } -}; - -struct IdentifierHashData -{ - IdentifierHashData(int numBits); - ~IdentifierHashData() { - free(entries); - } - - QBasicAtomicInt refCount; - int alloc; - int size; - int numBits; - IdentifierTable *identifierTable; - IdentifierHashEntry *entries; -}; - -struct IdentifierHashBase -{ - - IdentifierHashData *d; - - IdentifierHashBase() : d(0) {} - IdentifierHashBase(ExecutionEngine *engine); - inline IdentifierHashBase(const IdentifierHashBase &other); - inline ~IdentifierHashBase(); - inline IdentifierHashBase &operator=(const IdentifierHashBase &other); - - bool isEmpty() const { return !d; } - // ### - void reserve(int) {} - - inline int count() const; - bool contains(const Identifier *i) const; - bool contains(const QString &str) const; - bool contains(String *str) const; - -protected: - IdentifierHashEntry *addEntry(const Identifier *i); - const IdentifierHashEntry *lookup(const Identifier *identifier) const; - const IdentifierHashEntry *lookup(const QString &str) const; - const IdentifierHashEntry *lookup(String *str) const; - const Identifier *toIdentifier(const QString &str) const; - const Identifier *toIdentifier(String *str) const; -}; - - -template -struct IdentifierHash : public IdentifierHashBase -{ - IdentifierHash() - : IdentifierHashBase() {} - IdentifierHash(ExecutionEngine *engine) - : IdentifierHashBase(engine) {} - inline IdentifierHash(const IdentifierHash &other) - : IdentifierHashBase(other) {} - inline ~IdentifierHash() {} - inline IdentifierHash &operator=(const IdentifierHash &other) { - IdentifierHashBase::operator =(other); - return *this; - } - - void add(const QString &str, const T &value); - - inline T value(const QString &str) const; - inline T value(String *str) const; - QString findId(T value) const; -}; - -inline IdentifierHashBase::IdentifierHashBase(const IdentifierHashBase &other) -{ - d = other.d; - if (d) - d->refCount.ref(); -} - -inline IdentifierHashBase::~IdentifierHashBase() -{ - if (d && !d->refCount.deref()) - delete d; -} - -IdentifierHashBase &IdentifierHashBase::operator=(const IdentifierHashBase &other) -{ - if (other.d) - other.d->refCount.ref(); - if (d && !d->refCount.deref()) - delete d; - d = other.d; - return *this; -} - -inline int IdentifierHashBase::count() const -{ - return d ? d->size : 0; -} - -inline bool IdentifierHashBase::contains(const Identifier *i) const -{ - return lookup(i) != 0; -} - -inline bool IdentifierHashBase::contains(const QString &str) const -{ - return lookup(str) != 0; -} - -inline bool IdentifierHashBase::contains(String *str) const -{ - return lookup(str) != 0; -} - -template -void IdentifierHash::add(const QString &str, const T &value) -{ - IdentifierHashEntry *e = addEntry(toIdentifier(str)); - e->value = value; -} - -template -inline T IdentifierHash::value(const QString &str) const -{ - return lookup(str)->get((T*)0); -} - -template -inline T IdentifierHash::value(String *str) const -{ - return lookup(str)->get((T*)0); -} - - -template -QString IdentifierHash::findId(T value) const -{ - IdentifierHashEntry *e = d->entries; - IdentifierHashEntry *end = e + d->alloc; - while (e < end) { - if (e->identifier && e->get((T*)0) == value) - return e->identifier->string; - ++e; - } - return QString(); -} - - -} - -QT_END_NAMESPACE - -#endif diff --git a/src/qml/qml/v4/qv4identifiertable.cpp b/src/qml/qml/v4/qv4identifiertable.cpp deleted file mode 100644 index 5de2f893ef..0000000000 --- a/src/qml/qml/v4/qv4identifiertable.cpp +++ /dev/null @@ -1,184 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#include "qv4identifiertable_p.h" - -QT_BEGIN_NAMESPACE - -namespace QV4 { - -static const uchar prime_deltas[] = { - 0, 0, 1, 3, 1, 5, 3, 3, 1, 9, 7, 5, 3, 9, 25, 3, - 1, 21, 3, 21, 7, 15, 9, 5, 3, 29, 15, 0, 0, 0, 0, 0 -}; - -static inline int primeForNumBits(int numBits) -{ - return (1 << numBits) + prime_deltas[numBits]; -} - - -IdentifierTable::IdentifierTable(ExecutionEngine *engine) - : engine(engine) - , size(0) - , numBits(8) -{ - alloc = primeForNumBits(numBits); - entries = (String **)malloc(alloc*sizeof(String *)); - memset(entries, 0, alloc*sizeof(String *)); -} - -IdentifierTable::~IdentifierTable() -{ - free(entries); -} - -void IdentifierTable::addEntry(String *str) -{ - uint hash = str->hashValue(); - - if (str->subtype == String::StringType_ArrayIndex) - return; - - str->identifier = new Identifier; - str->identifier->string = str->toQString(); - str->identifier->hashValue = hash; - - bool grow = (alloc <= size*2); - - if (grow) { - ++numBits; - int newAlloc = primeForNumBits(numBits); - String **newEntries = (String **)malloc(newAlloc*sizeof(String *)); - memset(newEntries, 0, newAlloc*sizeof(String *)); - for (uint i = 0; i < alloc; ++i) { - String *e = entries[i]; - if (!e) - continue; - uint idx = e->stringHash % newAlloc; - while (newEntries[idx]) { - ++idx; - idx %= newAlloc; - } - newEntries[idx] = e; - } - free(entries); - entries = newEntries; - alloc = newAlloc; - } - - uint idx = hash % alloc; - while (entries[idx]) { - ++idx; - idx %= alloc; - } - entries[idx] = str; - ++size; -} - - - -String *IdentifierTable::insertString(const QString &s) -{ - uint hash = String::createHashValue(s.constData(), s.length()); - uint idx = hash % alloc; - while (String *e = entries[idx]) { - if (e->stringHash == hash && e->toQString() == s) - return e; - ++idx; - idx %= alloc; - } - - String *str = engine->newString(s); - addEntry(str); - return str; -} - - -Identifier *IdentifierTable::identifier(String *str) -{ - if (str->identifier) - return str->identifier; - uint hash = str->hashValue(); - if (str->subtype == String::StringType_ArrayIndex) - return 0; - - uint idx = hash % alloc; - while (String *e = entries[idx]) { - if (e->stringHash == hash && e->isEqualTo(str)) { - str->identifier = e->identifier; - return e->identifier; - } - ++idx; - idx %= alloc; - } - - addEntry(str); - return str->identifier; -} - -Identifier *IdentifierTable::identifier(const QString &s) -{ - return insertString(s)->identifier; -} - -Identifier *IdentifierTable::identifier(const char *s, int len) -{ - uint hash = String::createHashValue(s, len); - if (hash == UINT_MAX) - return identifier(QString::fromUtf8(s, len)); - - QLatin1String latin(s, len); - uint idx = hash % alloc; - while (String *e = entries[idx]) { - if (e->stringHash == hash && e->toQString() == latin) - return e->identifier; - ++idx; - idx %= alloc; - } - - String *str = engine->newString(QString::fromLatin1(s, len)); - addEntry(str); - return str->identifier; -} - -} - -QT_END_NAMESPACE diff --git a/src/qml/qml/v4/qv4identifiertable_p.h b/src/qml/qml/v4/qv4identifiertable_p.h deleted file mode 100644 index 0f9a5921f9..0000000000 --- a/src/qml/qml/v4/qv4identifiertable_p.h +++ /dev/null @@ -1,86 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QV4IDENTIFIERTABLE_H -#define QV4IDENTIFIERTABLE_H - -#include "qv4identifier_p.h" -#include "qv4string_p.h" -#include "qv4engine_p.h" -#include - -QT_BEGIN_NAMESPACE - -namespace QV4 { - -struct IdentifierTable -{ - ExecutionEngine *engine; - - int alloc; - int size; - int numBits; - String **entries; - - void addEntry(String *str); - -public: - - IdentifierTable(ExecutionEngine *engine); - ~IdentifierTable(); - - String *insertString(const QString &s); - - Identifier *identifier(String *str); - Identifier *identifier(const QString &s); - Identifier *identifier(const char *s, int len); - - void mark() { - for (int i = 0; i < alloc; ++i) - if (entries[i]) - entries[i]->mark(); - } -}; - -} - -QT_END_NAMESPACE - -#endif diff --git a/src/qml/qml/v4/qv4include.cpp b/src/qml/qml/v4/qv4include.cpp deleted file mode 100644 index 4fd7bb14c7..0000000000 --- a/src/qml/qml/v4/qv4include.cpp +++ /dev/null @@ -1,232 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4include_p.h" - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE - -QV4Include::QV4Include(const QUrl &url, QV8Engine *engine, QQmlContextData *context, - const QV4::Value &qmlglobal, const QV4::Value &callback) - : v4(QV8Engine::getV4(engine)), m_network(0), m_reply(0), m_url(url), m_redirectCount(0), m_context(context) -{ - m_qmlglobal = qmlglobal; - if (callback.asFunctionObject()) - m_callbackFunction = callback; - - m_resultObject = resultValue(v4); - - m_network = engine->networkAccessManager(); - - QNetworkRequest request; - request.setUrl(url); - - m_reply = m_network->get(request); - QObject::connect(m_reply, SIGNAL(finished()), this, SLOT(finished())); -} - -QV4Include::~QV4Include() -{ - delete m_reply; m_reply = 0; -} - -QV4::Value QV4Include::resultValue(QV4::ExecutionEngine *v4, Status status) -{ - - // XXX It seems inefficient to create this object from scratch each time. - QV4::Object *o = v4->newObject(); - o->put(v4->newString("OK"), QV4::Value::fromInt32(Ok)); - o->put(v4->newString("LOADING"), QV4::Value::fromInt32(Loading)); - o->put(v4->newString("NETWORK_ERROR"), QV4::Value::fromInt32(NetworkError)); - o->put(v4->newString("EXCEPTION"), QV4::Value::fromInt32(Exception)); - - o->put(v4->newString("status"), QV4::Value::fromInt32(status)); - - return QV4::Value::fromObject(o); -} - -void QV4Include::callback(const QV4::Value &callback, const QV4::Value &status) -{ - QV4::FunctionObject *f = callback.asFunctionObject(); - if (!f) - return; - - QV4::Value args[] = { status }; - QV4::ExecutionContext *ctx = f->engine()->current; - try { - f->call(QV4::Value::fromObject(f->engine()->globalObject), args, 1); - } catch (QV4::Exception &e) { - e.accept(ctx); - } -} - -QV4::Value QV4Include::result() -{ - return m_resultObject.value(); -} - -#define INCLUDE_MAXIMUM_REDIRECT_RECURSION 15 -void QV4Include::finished() -{ - m_redirectCount++; - - if (m_redirectCount < INCLUDE_MAXIMUM_REDIRECT_RECURSION) { - QVariant redirect = m_reply->attribute(QNetworkRequest::RedirectionTargetAttribute); - if (redirect.isValid()) { - m_url = m_url.resolved(redirect.toUrl()); - delete m_reply; - - QNetworkRequest request; - request.setUrl(m_url); - - m_reply = m_network->get(request); - QObject::connect(m_reply, SIGNAL(finished()), this, SLOT(finished())); - return; - } - } - - if (m_reply->error() == QNetworkReply::NoError) { - QByteArray data = m_reply->readAll(); - - QString code = QString::fromUtf8(data); - QQmlScript::Parser::extractPragmas(code); - - QV4::Script script(v4, m_qmlglobal.value().asObject(), code, m_url.toString()); - - QV4::ExecutionContext *ctx = v4->current; - QV4::Object *o = m_resultObject.value().asObject(); - try { - script.parse(); - script.run(); - o->put(v4->newString("status"), QV4::Value::fromInt32(Ok)); - } catch (QV4::Exception &e) { - e.accept(ctx); - o->put(v4->newString("status"), QV4::Value::fromInt32(Exception)); - o->put(v4->newString("exception"), e.value()); - } - } else { - m_resultObject.value().asObject()->put(v4->newString("status"), QV4::Value::fromInt32(NetworkError)); - } - - callback(m_callbackFunction.value(), m_resultObject.value()); - - disconnect(); - deleteLater(); -} - -/* - Documented in qv8engine.cpp -*/ -QV4::Value QV4Include::include(QV4::SimpleCallContext *ctx) -{ - if (!ctx->argumentCount) - return QV4::Value::undefinedValue(); - - QV4::ExecutionEngine *v4 = ctx->engine; - QV8Engine *engine = v4->v8Engine; - QQmlContextData *context = QV4::QmlContextWrapper::callingContext(v4); - - if (!context || !context->isJSContext) - V4THROW_ERROR("Qt.include(): Can only be called from JavaScript files"); - - QUrl url(ctx->engine->resolvedUrl(ctx->arguments[0].toQString())); - - QV4::Value callbackFunction = QV4::Value::undefinedValue(); - if (ctx->argumentCount >= 2 && ctx->arguments[1].asFunctionObject()) - callbackFunction = ctx->arguments[1]; - - QString localFile = QQmlFile::urlToLocalFileOrQrc(url); - - QV4::Value result = QV4::Value::undefinedValue(); - - if (localFile.isEmpty()) { - - QV4Include *i = new QV4Include(url, engine, context, - QV4::Value::fromObject(v4->qmlContextObject()), - callbackFunction); - result = i->result(); - - } else { - - QFile f(localFile); - - if (f.open(QIODevice::ReadOnly)) { - QByteArray data = f.readAll(); - QString code = QString::fromUtf8(data); - QQmlScript::Parser::extractPragmas(code); - - QV4::Object *qmlglobal = v4->qmlContextObject(); - QV4::Script script(v4, qmlglobal, code, url.toString()); - - QV4::ExecutionContext *ctx = v4->current; - try { - script.parse(); - script.run(); - result = resultValue(v4, Ok); - } catch (QV4::Exception &e) { - e.accept(ctx); - result = resultValue(v4, Exception); - result.asObject()->put(v4->newString("exception"), e.value()); - } - } else { - result = resultValue(v4, NetworkError); - } - - callback(callbackFunction, result); - } - - return result; -} - -QT_END_NAMESPACE diff --git a/src/qml/qml/v4/qv4include_p.h b/src/qml/qml/v4/qv4include_p.h deleted file mode 100644 index d6bbcd1a60..0000000000 --- a/src/qml/qml/v4/qv4include_p.h +++ /dev/null @@ -1,113 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QV4INCLUDE_P_H -#define QV4INCLUDE_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include -#include - -#include - -#include -#include - -QT_BEGIN_NAMESPACE - -class QQmlEngine; -class QNetworkAccessManager; -class QNetworkReply; -class QV8Engine; -class QV4Include : public QObject -{ - Q_OBJECT -public: - enum Status { - Ok = 0, - Loading = 1, - NetworkError = 2, - Exception = 3 - }; - - static QV4::Value include(QV4::SimpleCallContext *ctx); - -private slots: - void finished(); - -private: - QV4Include(const QUrl &url, QV8Engine *engine, QQmlContextData *context, - const QV4::Value &qmlglobal, const QV4::Value &callback); - ~QV4Include(); - - QV4::Value result(); - - static QV4::Value resultValue(QV4::ExecutionEngine *v4, Status status = Loading); - static void callback(const QV4::Value &callback, const QV4::Value &status); - - QV4::ExecutionEngine *v4; - QNetworkAccessManager *m_network; - QPointer m_reply; - - QUrl m_url; - int m_redirectCount; - - QV4::PersistentValue m_callbackFunction; - QV4::PersistentValue m_resultObject; - - QQmlGuardedContextData m_context; - QV4::PersistentValue m_qmlglobal; -}; - -QT_END_NAMESPACE - -#endif - diff --git a/src/qml/qml/v4/qv4internalclass.cpp b/src/qml/qml/v4/qv4internalclass.cpp deleted file mode 100644 index f4edc99545..0000000000 --- a/src/qml/qml/v4/qv4internalclass.cpp +++ /dev/null @@ -1,296 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include -#include -#include -#include -#include "qv4object_p.h" -#include "qv4identifiertable_p.h" - -QT_BEGIN_NAMESPACE - -uint QV4::qHash(const QV4::InternalClassTransition &t, uint) -{ - return t.id->hashValue ^ t.flags; -} - -using namespace QV4; - -static const uchar prime_deltas[] = { - 0, 0, 1, 3, 1, 5, 3, 3, 1, 9, 7, 5, 3, 9, 25, 3, - 1, 21, 3, 21, 7, 15, 9, 5, 3, 29, 15, 0, 0, 0, 0, 0 -}; - -static inline int primeForNumBits(int numBits) -{ - return (1 << numBits) + prime_deltas[numBits]; -} - -PropertyHashData::PropertyHashData(int numBits) - : numBits(numBits) - , size(0) -{ - refCount.store(1); - alloc = primeForNumBits(numBits); - entries = (PropertyHash::Entry *)malloc(alloc*sizeof(PropertyHash::Entry)); - memset(entries, 0, alloc*sizeof(PropertyHash::Entry)); -} - -void PropertyHash::addEntry(const PropertyHash::Entry &entry, int classSize) -{ - // fill up to max 50% - bool grow = (d->alloc <= d->size*2); - - if (classSize < d->size || grow) { - PropertyHashData *dd = new PropertyHashData(grow ? d->numBits + 1 : d->numBits); - for (uint i = 0; i < d->alloc; ++i) { - const Entry &e = d->entries[i]; - if (!e.identifier || e.index >= classSize) - continue; - uint idx = e.identifier->hashValue % dd->alloc; - while (dd->entries[idx].identifier) { - ++idx; - idx %= dd->alloc; - } - dd->entries[idx] = e; - } - dd->size = classSize; - assert(d->refCount.load() > 1); - d->refCount.deref(); - d = dd; - } - - uint idx = entry.identifier->hashValue % d->alloc; - while (d->entries[idx].identifier) { - ++idx; - idx %= d->alloc; - } - d->entries[idx] = entry; - ++d->size; -} - -uint PropertyHash::lookup(const Identifier *identifier) const -{ - assert(d->entries); - - uint idx = identifier->hashValue % d->alloc; - while (1) { - if (d->entries[idx].identifier == identifier) - return d->entries[idx].index; - if (!d->entries[idx].identifier) - return UINT_MAX; - ++idx; - idx %= d->alloc; - } -} - - -InternalClass::InternalClass(const QV4::InternalClass &other) - : engine(other.engine) - , propertyTable(other.propertyTable) - , nameMap(other.nameMap) - , propertyData(other.propertyData) - , transitions() - , m_sealed(0) - , m_frozen(0) - , size(other.size) -{ -} - -// ### Should we build this up from the empty class to avoid duplication? -InternalClass *InternalClass::changeMember(String *string, PropertyAttributes data, uint *index) -{ -// qDebug() << "InternalClass::changeMember()" << string->toQString() << hex << (uint)data.m_all; - data.resolve(); - uint idx = find(string); - if (index) - *index = idx; - - assert(idx != UINT_MAX); - - if (data == propertyData[idx]) - return this; - - - Transition t = { string->identifier, (int)data.flags() }; - QHash::const_iterator tit = transitions.constFind(t); - if (tit != transitions.constEnd()) - return tit.value(); - - // create a new class and add it to the tree - InternalClass *newClass = engine->newClass(*this); - newClass->propertyData[idx] = data; - return newClass; - -} - -InternalClass *InternalClass::addMember(String *string, PropertyAttributes data, uint *index) -{ -// qDebug() << "InternalClass::addMember()" << string->toQString() << size << hex << (uint)data.m_all << data.type(); - data.resolve(); - engine->identifierTable->identifier(string); - - if (propertyTable.lookup(string->identifier) < size) - return changeMember(string, data, index); - - Transition t = { string->identifier, (int)data.flags() }; - QHash::const_iterator tit = transitions.constFind(t); - - if (index) - *index = size; - if (tit != transitions.constEnd()) - return tit.value(); - - // create a new class and add it to the tree - InternalClass *newClass = engine->newClass(*this); - PropertyHash::Entry e = { string->identifier, size }; - newClass->propertyTable.addEntry(e, size); - - // The incoming string can come from anywhere, so make sure to - // store a string in the nameMap that's guaranteed to get - // marked properly during GC. - String *name = engine->newIdentifier(string->toQString()); - newClass->nameMap.append(name); - - newClass->propertyData.append(data); - ++newClass->size; - transitions.insert(t, newClass); - return newClass; -} - -void InternalClass::removeMember(Object *object, Identifier *id) -{ - int propIdx = propertyTable.lookup(id); - assert(propIdx < size); - - Transition t = { id, -1 }; - QHash::const_iterator tit = transitions.constFind(t); - - if (tit != transitions.constEnd()) { - object->internalClass = tit.value(); - return; - } - - // create a new class and add it to the tree - object->internalClass = engine->emptyClass; - for (int i = 0; i < nameMap.size(); ++i) { - if (i == propIdx) - continue; - object->internalClass = object->internalClass->addMember(nameMap.at(i), propertyData.at(i)); - } - - transitions.insert(t, object->internalClass); -} - -uint InternalClass::find(String *string) -{ - engine->identifierTable->identifier(string); - const Identifier *id = string->identifier; - - uint index = propertyTable.lookup(id); - if (index < size) - return index; - - return UINT_MAX; -} - -InternalClass *InternalClass::sealed() -{ - if (m_sealed) - return m_sealed; - - m_sealed = engine->emptyClass; - for (int i = 0; i < nameMap.size(); ++i) { - PropertyAttributes attrs = propertyData.at(i); - attrs.setConfigurable(false); - m_sealed = m_sealed->addMember(nameMap.at(i), attrs); - } - - m_sealed->m_sealed = m_sealed; - return m_sealed; -} - -InternalClass *InternalClass::frozen() -{ - if (m_frozen) - return m_frozen; - - m_frozen = engine->emptyClass; - for (int i = 0; i < nameMap.size(); ++i) { - PropertyAttributes attrs = propertyData.at(i); - attrs.setWritable(false); - attrs.setConfigurable(false); - m_frozen = m_frozen->addMember(nameMap.at(i), attrs); - } - - m_frozen->m_frozen = m_frozen; - m_frozen->m_sealed = m_frozen; - return m_frozen; -} - -void InternalClass::destroy() -{ - if (!engine) - return; - engine = 0; - - // Free the memory of the hashes/vectors by calling clear(), which - // re-assigns them to the shared null instance. Therefore Internalclass - // doesn't need a destructor to be called. - propertyTable.~PropertyHash(); - nameMap.clear(); - propertyData.clear(); - - if (m_sealed) - m_sealed->destroy(); - - if (m_frozen) - m_frozen->destroy(); - - for (QHash::ConstIterator it = transitions.begin(), end = transitions.end(); - it != end; ++it) - it.value()->destroy(); - - transitions.clear(); -} - -QT_END_NAMESPACE diff --git a/src/qml/qml/v4/qv4internalclass_p.h b/src/qml/qml/v4/qv4internalclass_p.h deleted file mode 100644 index fc6c5352b1..0000000000 --- a/src/qml/qml/v4/qv4internalclass_p.h +++ /dev/null @@ -1,154 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QV4INTERNALCLASS_H -#define QV4INTERNALCLASS_H - -#include -#include -#include "qv4global_p.h" - -QT_BEGIN_NAMESPACE - -namespace QV4 { - -struct String; -struct ExecutionEngine; -struct Object; -struct Identifier; - -struct PropertyHashData; -struct PropertyHash -{ - struct Entry { - const Identifier *identifier; - uint index; - }; - - PropertyHashData *d; - - inline PropertyHash(); - inline PropertyHash(const PropertyHash &other); - inline ~PropertyHash(); - - void addEntry(const Entry &entry, int classSize); - uint lookup(const Identifier *identifier) const; - -private: - PropertyHash &operator=(const PropertyHash &other); -}; - -struct PropertyHashData -{ - PropertyHashData(int numBits); - ~PropertyHashData() { - free(entries); - } - - QBasicAtomicInt refCount; - int alloc; - int size; - int numBits; - PropertyHash::Entry *entries; -}; - -inline PropertyHash::PropertyHash() -{ - d = new PropertyHashData(3); -} - -inline PropertyHash::PropertyHash(const PropertyHash &other) -{ - d = other.d; - d->refCount.ref(); -} - -inline PropertyHash::~PropertyHash() -{ - if (!d->refCount.deref()) - delete d; -} - -struct InternalClassTransition -{ - Identifier *id; - int flags; - - bool operator==(const InternalClassTransition &other) const - { return id == other.id && flags == other.flags; } -}; -uint qHash(const QV4::InternalClassTransition &t, uint = 0); - -struct InternalClass { - ExecutionEngine *engine; - PropertyHash propertyTable; // id to valueIndex - QVector nameMap; - - QVector propertyData; - - typedef InternalClassTransition Transition; - QHash transitions; // id to next class, positive means add, negative delete - - InternalClass *m_sealed; - InternalClass *m_frozen; - - uint size; - - InternalClass *addMember(String *string, PropertyAttributes data, uint *index = 0); - InternalClass *changeMember(String *string, PropertyAttributes data, uint *index = 0); - void removeMember(Object *object, Identifier *id); - uint find(String *s); - - InternalClass *sealed(); - InternalClass *frozen(); - - void destroy(); - -private: - friend struct ExecutionEngine; - InternalClass(ExecutionEngine *engine) : engine(engine), m_sealed(0), m_frozen(0), size(0) {} - InternalClass(const InternalClass &other); -}; - -} - -QT_END_NAMESPACE - -#endif diff --git a/src/qml/qml/v4/qv4isel_masm.cpp b/src/qml/qml/v4/qv4isel_masm.cpp deleted file mode 100644 index 4317ba0d54..0000000000 --- a/src/qml/qml/v4/qv4isel_masm.cpp +++ /dev/null @@ -1,1466 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4isel_masm_p.h" -#include "qv4runtime_p.h" -#include "qv4object_p.h" -#include "qv4functionobject_p.h" -#include "qv4regexpobject_p.h" -#include "qv4unwindhelper_p.h" -#include "qv4lookup_p.h" -#include "qv4function_p.h" -#include "qv4ssa_p.h" -#include "qv4exception_p.h" - -#include -#include - -#include -#include - -#if USE(UDIS86) -# include -#endif - -using namespace QQmlJS; -using namespace QQmlJS::MASM; -using namespace QV4; - -namespace { -class ConvertTemps: protected V4IR::StmtVisitor, protected V4IR::ExprVisitor -{ - int _nextFreeStackSlot; - QHash _stackSlotForTemp; - - void renumber(V4IR::Temp *t) - { - if (t->kind != V4IR::Temp::VirtualRegister) - return; - - int stackSlot = _stackSlotForTemp.value(*t, -1); - if (stackSlot == -1) { - stackSlot = _nextFreeStackSlot++; - _stackSlotForTemp[*t] = stackSlot; - } - - t->kind = V4IR::Temp::StackSlot; - t->index = stackSlot; - } - -public: - ConvertTemps() - : _nextFreeStackSlot(0) - {} - - void toStackSlots(V4IR::Function *function) - { - _stackSlotForTemp.reserve(function->tempCount); - - foreach (V4IR::BasicBlock *bb, function->basicBlocks) - foreach (V4IR::Stmt *s, bb->statements) - s->accept(this); - - function->tempCount = _nextFreeStackSlot; - } - -protected: - virtual void visitConst(V4IR::Const *) {} - virtual void visitString(V4IR::String *) {} - virtual void visitRegExp(V4IR::RegExp *) {} - virtual void visitName(V4IR::Name *) {} - virtual void visitTemp(V4IR::Temp *e) { renumber(e); } - virtual void visitClosure(V4IR::Closure *) {} - virtual void visitConvert(V4IR::Convert *e) { e->expr->accept(this); } - virtual void visitUnop(V4IR::Unop *e) { e->expr->accept(this); } - virtual void visitBinop(V4IR::Binop *e) { e->left->accept(this); e->right->accept(this); } - virtual void visitCall(V4IR::Call *e) { - e->base->accept(this); - for (V4IR::ExprList *it = e->args; it; it = it->next) - it->expr->accept(this); - } - virtual void visitNew(V4IR::New *e) { - e->base->accept(this); - for (V4IR::ExprList *it = e->args; it; it = it->next) - it->expr->accept(this); - } - virtual void visitSubscript(V4IR::Subscript *e) { e->base->accept(this); e->index->accept(this); } - virtual void visitMember(V4IR::Member *e) { e->base->accept(this); } - virtual void visitExp(V4IR::Exp *s) { s->expr->accept(this); } - virtual void visitMove(V4IR::Move *s) { s->target->accept(this); s->source->accept(this); } - virtual void visitJump(V4IR::Jump *) {} - virtual void visitCJump(V4IR::CJump *s) { s->cond->accept(this); } - virtual void visitRet(V4IR::Ret *s) { s->expr->accept(this); } - virtual void visitTry(V4IR::Try *s) { s->exceptionVar->accept(this); } - virtual void visitPhi(V4IR::Phi *) { Q_UNREACHABLE(); } -}; -} // anonymous namespace - -/* Platform/Calling convention/Architecture specific section */ - -#if CPU(X86_64) -static const Assembler::RegisterID calleeSavedRegisters[] = { - // Not used: JSC::X86Registers::rbx, - // Not used: JSC::X86Registers::r10, - JSC::X86Registers::r12, // LocalsRegister - // Not used: JSC::X86Registers::r13, - JSC::X86Registers::r14 // ContextRegister - // Not used: JSC::X86Registers::r15, -}; -#endif - -#if CPU(X86) -static const Assembler::RegisterID calleeSavedRegisters[] = { - // Not used: JSC::X86Registers::ebx, - JSC::X86Registers::esi, // ContextRegister - JSC::X86Registers::edi // LocalsRegister -}; -#endif - -#if CPU(ARM) -static const Assembler::RegisterID calleeSavedRegisters[] = { - // ### FIXME: remove unused registers. - // Keep these in reverse order and make sure to also edit the unwind program in - // qv4unwindhelper_p-arm.h when changing this list. - JSC::ARMRegisters::r12, - JSC::ARMRegisters::r10, - JSC::ARMRegisters::r9, - JSC::ARMRegisters::r8, - JSC::ARMRegisters::r7, - JSC::ARMRegisters::r6, - JSC::ARMRegisters::r5, - JSC::ARMRegisters::r4 -}; -#endif - -const int Assembler::calleeSavedRegisterCount = sizeof(calleeSavedRegisters) / sizeof(calleeSavedRegisters[0]); - -/* End of platform/calling convention/architecture specific section */ - - -const Assembler::VoidType Assembler::Void; - -Assembler::Assembler(V4IR::Function* function, QV4::Function *vmFunction, QV4::ExecutionEngine *engine) - : _function(function), _vmFunction(vmFunction), _engine(engine), _nextBlock(0) -{ -} - -void Assembler::registerBlock(V4IR::BasicBlock* block, V4IR::BasicBlock *nextBlock) -{ - _addrs[block] = label(); - _nextBlock = nextBlock; -} - -void Assembler::jumpToBlock(V4IR::BasicBlock* current, V4IR::BasicBlock *target) -{ - if (target != _nextBlock) - _patches[target].append(jump()); -} - -void Assembler::addPatch(V4IR::BasicBlock* targetBlock, Jump targetJump) -{ - _patches[targetBlock].append(targetJump); -} - -void Assembler::addPatch(DataLabelPtr patch, Label target) -{ - DataLabelPatch p; - p.dataLabel = patch; - p.target = target; - _dataLabelPatches.append(p); -} - -void Assembler::addPatch(DataLabelPtr patch, V4IR::BasicBlock *target) -{ - _labelPatches[target].append(patch); -} - -Assembler::Pointer Assembler::loadTempAddress(RegisterID reg, V4IR::Temp *t) -{ - int32_t offset = 0; - int scope = t->scope; - QV4::Function *f = _vmFunction; - RegisterID context = ContextRegister; - if (scope) { - loadPtr(Address(ContextRegister, offsetof(ExecutionContext, outer)), ScratchRegister); - --scope; - f = f->outer; - context = ScratchRegister; - while (scope) { - loadPtr(Address(context, offsetof(ExecutionContext, outer)), context); - f = f->outer; - --scope; - } - } - switch (t->kind) { - case V4IR::Temp::Formal: - case V4IR::Temp::ScopedFormal: { - loadPtr(Address(context, offsetof(CallContext, arguments)), reg); - offset = t->index * sizeof(Value); - } break; - case V4IR::Temp::Local: - case V4IR::Temp::ScopedLocal: { - loadPtr(Address(context, offsetof(CallContext, locals)), reg); - offset = t->index * sizeof(Value); - } break; - case V4IR::Temp::StackSlot: { - assert(t->scope == 0); - const int arg = _function->maxNumberOfArguments + t->index + 1; - offset = - sizeof(Value) * (arg + 1); - offset -= sizeof(void*) * calleeSavedRegisterCount; - reg = LocalsRegister; - } break; - default: - Q_UNIMPLEMENTED(); - } - return Pointer(reg, offset); -} - -template -void Assembler::copyValue(Result result, Source source) -{ -#ifdef VALUE_FITS_IN_REGISTER - // Use ReturnValueRegister as "scratch" register because loadArgument - // and storeArgument are functions that may need a scratch register themselves. - loadArgumentInRegister(source, ReturnValueRegister); - storeReturnValue(result); -#else - loadDouble(source, FPGpr0); - storeDouble(FPGpr0, result); -#endif -} - -template -void Assembler::copyValue(Result result, V4IR::Expr* source) -{ -#ifdef VALUE_FITS_IN_REGISTER - // Use ReturnValueRegister as "scratch" register because loadArgument - // and storeArgument are functions that may need a scratch register themselves. - loadArgumentInRegister(source, ReturnValueRegister); - storeReturnValue(result); -#else - if (V4IR::Temp *temp = source->asTemp()) { - loadDouble(temp, FPGpr0); - storeDouble(FPGpr0, result); - } else if (V4IR::Const *c = source->asConst()) { - QV4::Value v = convertToValue(c); - storeValue(v, result); - } else { - assert(! "not implemented"); - } -#endif -} - - -void Assembler::storeValue(QV4::Value value, V4IR::Temp* destination) -{ - Address addr = loadTempAddress(ScratchRegister, destination); - storeValue(value, addr); -} - -int Assembler::calculateStackFrameSize(int locals) -{ - const int stackSpaceAllocatedOtherwise = StackSpaceAllocatedUponFunctionEntry - + RegisterSize; // saved StackFrameRegister - - // space for the locals and the callee saved registers - int frameSize = locals * sizeof(QV4::Value) + sizeof(void*) * calleeSavedRegisterCount; - - frameSize = WTF::roundUpToMultipleOf(StackAlignment, frameSize + stackSpaceAllocatedOtherwise); - frameSize -= stackSpaceAllocatedOtherwise; - - return frameSize; -} - -void Assembler::enterStandardStackFrame(int locals) -{ - platformEnterStandardStackFrame(); - - // ### FIXME: Handle through calleeSavedRegisters mechanism - // or eliminate StackFrameRegister altogether. - push(StackFrameRegister); - move(StackPointerRegister, StackFrameRegister); - - int frameSize = calculateStackFrameSize(locals); - - subPtr(TrustedImm32(frameSize), StackPointerRegister); - - for (int i = 0; i < calleeSavedRegisterCount; ++i) - storePtr(calleeSavedRegisters[i], Address(StackFrameRegister, -(i + 1) * sizeof(void*))); - - move(StackFrameRegister, LocalsRegister); -} - -void Assembler::leaveStandardStackFrame(int locals) -{ - // restore the callee saved registers - for (int i = calleeSavedRegisterCount - 1; i >= 0; --i) - loadPtr(Address(StackFrameRegister, -(i + 1) * sizeof(void*)), calleeSavedRegisters[i]); - - int frameSize = calculateStackFrameSize(locals); - // Work around bug in ARMv7Assembler.h where add32(imm, sp, sp) doesn't - // work well for large immediates. -#if CPU(ARM_THUMB2) - move(TrustedImm32(frameSize), JSC::ARMRegisters::r3); - add32(JSC::ARMRegisters::r3, StackPointerRegister); -#else - addPtr(TrustedImm32(frameSize), StackPointerRegister); -#endif - - pop(StackFrameRegister); - platformLeaveStandardStackFrame(); -} - - - -#define OP(op) \ - { isel_stringIfy(op), op, 0, 0, 0 } -#define OPCONTEXT(op) \ - { isel_stringIfy(op), 0, op, 0, 0 } - -#define INLINE_OP(op, memOp, immOp) \ - { isel_stringIfy(op), op, 0, memOp, immOp } -#define INLINE_OPCONTEXT(op, memOp, immOp) \ - { isel_stringIfy(op), 0, op, memOp, immOp } - -#define NULL_OP \ - { 0, 0, 0, 0, 0 } - -const Assembler::BinaryOperationInfo Assembler::binaryOperations[QQmlJS::V4IR::LastAluOp + 1] = { - NULL_OP, // OpInvalid - NULL_OP, // OpIfTrue - NULL_OP, // OpNot - NULL_OP, // OpUMinus - NULL_OP, // OpUPlus - NULL_OP, // OpCompl - NULL_OP, // OpIncrement - NULL_OP, // OpDecrement - - INLINE_OP(__qmljs_bit_and, &Assembler::inline_and32, &Assembler::inline_and32), // OpBitAnd - INLINE_OP(__qmljs_bit_or, &Assembler::inline_or32, &Assembler::inline_or32), // OpBitOr - INLINE_OP(__qmljs_bit_xor, &Assembler::inline_xor32, &Assembler::inline_xor32), // OpBitXor - - INLINE_OPCONTEXT(__qmljs_add, &Assembler::inline_add32, &Assembler::inline_add32), // OpAdd - INLINE_OP(__qmljs_sub, &Assembler::inline_sub32, &Assembler::inline_sub32), // OpSub - INLINE_OP(__qmljs_mul, &Assembler::inline_mul32, &Assembler::inline_mul32), // OpMul - - OP(__qmljs_div), // OpDiv - OP(__qmljs_mod), // OpMod - - INLINE_OP(__qmljs_shl, &Assembler::inline_shl32, &Assembler::inline_shl32), // OpLShift - INLINE_OP(__qmljs_shr, &Assembler::inline_shr32, &Assembler::inline_shr32), // OpRShift - INLINE_OP(__qmljs_ushr, &Assembler::inline_ushr32, &Assembler::inline_ushr32), // OpURShift - - OP(__qmljs_gt), // OpGt - OP(__qmljs_lt), // OpLt - OP(__qmljs_ge), // OpGe - OP(__qmljs_le), // OpLe - OP(__qmljs_eq), // OpEqual - OP(__qmljs_ne), // OpNotEqual - OP(__qmljs_se), // OpStrictEqual - OP(__qmljs_sne), // OpStrictNotEqual - - OPCONTEXT(__qmljs_instanceof), // OpInstanceof - OPCONTEXT(__qmljs_in), // OpIn - - NULL_OP, // OpAnd - NULL_OP // OpOr -}; - -void Assembler::generateBinOp(V4IR::AluOp operation, V4IR::Temp* target, V4IR::Temp *left, V4IR::Temp *right) -{ - const BinaryOperationInfo& info = binaryOperations[operation]; - if (!info.fallbackImplementation && !info.contextImplementation) { - assert(!"unreachable"); - return; - } - - Value leftConst = Value::undefinedValue(); - Value rightConst = Value::undefinedValue(); - - bool canDoInline = info.inlineMemRegOp && info.inlineImmRegOp; - - if (canDoInline) { - if (left->asConst()) { - leftConst = convertToValue(left->asConst()); - canDoInline = canDoInline && leftConst.tryIntegerConversion(); - } - if (right->asConst()) { - rightConst = convertToValue(right->asConst()); - canDoInline = canDoInline && rightConst.tryIntegerConversion(); - } - } - - Jump binOpFinished; - - if (canDoInline) { - - Jump leftTypeCheck; - if (left->asTemp()) { - Address typeAddress = loadTempAddress(ScratchRegister, left->asTemp()); - typeAddress.offset += offsetof(QV4::Value, tag); - leftTypeCheck = branch32(NotEqual, typeAddress, TrustedImm32(QV4::Value::_Integer_Type)); - } - - Jump rightTypeCheck; - if (right->asTemp()) { - Address typeAddress = loadTempAddress(ScratchRegister, right->asTemp()); - typeAddress.offset += offsetof(QV4::Value, tag); - rightTypeCheck = branch32(NotEqual, typeAddress, TrustedImm32(QV4::Value::_Integer_Type)); - } - - if (left->asTemp()) { - Address leftValue = loadTempAddress(ScratchRegister, left->asTemp()); - leftValue.offset += offsetof(QV4::Value, int_32); - load32(leftValue, IntegerOpRegister); - } else { // left->asConst() - move(TrustedImm32(leftConst.integerValue()), IntegerOpRegister); - } - - Jump overflowCheck; - - if (right->asTemp()) { - Address rightValue = loadTempAddress(ScratchRegister, right->asTemp()); - rightValue.offset += offsetof(QV4::Value, int_32); - - overflowCheck = (this->*info.inlineMemRegOp)(rightValue, IntegerOpRegister); - } else { // right->asConst() - overflowCheck = (this->*info.inlineImmRegOp)(TrustedImm32(rightConst.integerValue()), IntegerOpRegister); - } - - Address resultAddr = loadTempAddress(ScratchRegister, target); - Address resultValueAddr = resultAddr; - resultValueAddr.offset += offsetof(QV4::Value, int_32); - store32(IntegerOpRegister, resultValueAddr); - - Address resultTypeAddr = resultAddr; - resultTypeAddr.offset += offsetof(QV4::Value, tag); - store32(TrustedImm32(QV4::Value::_Integer_Type), resultTypeAddr); - - binOpFinished = jump(); - - if (leftTypeCheck.isSet()) - leftTypeCheck.link(this); - if (rightTypeCheck.isSet()) - rightTypeCheck.link(this); - if (overflowCheck.isSet()) - overflowCheck.link(this); - } - - // Fallback - if (info.contextImplementation) - generateFunctionCallImp(Assembler::Void, info.name, info.contextImplementation, ContextRegister, - Assembler::PointerToValue(target), Assembler::Reference(left), Assembler::Reference(right)); - else - generateFunctionCallImp(Assembler::Void, info.name, info.fallbackImplementation, - Assembler::PointerToValue(target), Assembler::Reference(left), Assembler::Reference(right)); - - if (binOpFinished.isSet()) - binOpFinished.link(this); -} -#if OS(LINUX) || OS(MAC_OS_X) -static void printDisassembledOutputWithCalls(const char* output, const QHash& functions, - const QVector &identifiers) -{ - QByteArray processedOutput(output); - for (QHash::ConstIterator it = functions.begin(), end = functions.end(); - it != end; ++it) { - QByteArray ptrString = QByteArray::number(quintptr(it.key()), 16); - ptrString.prepend("0x"); - processedOutput = processedOutput.replace(ptrString, it.value()); - } - for (QVector::ConstIterator it = identifiers.begin(), end = identifiers.end(); - it != end; ++it) { - QByteArray ptrString = QByteArray::number(quintptr(*it), 16); - ptrString.prepend("0x"); - QByteArray replacement = "\"" + (*it)->toQString().toUtf8() + "\""; - processedOutput = processedOutput.replace(ptrString, replacement); - } - fprintf(stderr, "%s\n", processedOutput.constData()); -} -#endif - -void Assembler::recordLineNumber(int lineNumber) -{ - CodeLineNumerMapping mapping; - mapping.location = label(); - mapping.lineNumber = lineNumber; - codeLineNumberMappings << mapping; -} - - -void Assembler::link(QV4::Function *vmFunc) -{ - Label endOfCode = label(); -#if defined(Q_PROCESSOR_ARM) && !defined(Q_OS_IOS) - // Let the ARM exception table follow right after that - for (int i = 0, nops = UnwindHelper::unwindInfoSize() / 2; i < nops; ++i) - nop(); -#endif - - { - QHashIterator > it(_patches); - while (it.hasNext()) { - it.next(); - V4IR::BasicBlock *block = it.key(); - Label target = _addrs.value(block); - assert(target.isSet()); - foreach (Jump jump, it.value()) - jump.linkTo(target, this); - } - } - - JSC::JSGlobalData dummy(_engine->executableAllocator); - JSC::LinkBuffer linkBuffer(dummy, this, 0); - vmFunc->codeSize = linkBuffer.offsetOf(endOfCode); - - vmFunc->lineNumberMappings.resize(codeLineNumberMappings.count()); - for (int i = 0; i < codeLineNumberMappings.count(); ++i) { - QV4::LineNumberMapping mapping; - mapping.codeOffset = linkBuffer.offsetOf(codeLineNumberMappings.at(i).location); - mapping.lineNumber = codeLineNumberMappings.at(i).lineNumber; - vmFunc->lineNumberMappings[i] = mapping; - } - - QHash functions; - foreach (CallToLink ctl, _callsToLink) { - linkBuffer.link(ctl.call, ctl.externalFunction); - functions[ctl.externalFunction.value()] = ctl.functionName; - } - - foreach (const DataLabelPatch &p, _dataLabelPatches) - linkBuffer.patch(p.dataLabel, linkBuffer.locationOf(p.target)); - - { - QHashIterator > it(_labelPatches); - while (it.hasNext()) { - it.next(); - V4IR::BasicBlock *block = it.key(); - Label target = _addrs.value(block); - assert(target.isSet()); - foreach (DataLabelPtr label, it.value()) - linkBuffer.patch(label, linkBuffer.locationOf(target)); - } - } - -#if defined(Q_PROCESSOR_ARM) && !defined(Q_OS_IOS) - UnwindHelper::writeARMUnwindInfo(linkBuffer.debugAddress(), linkBuffer.offsetOf(endOfCode)); -#endif - - static bool showCode = !qgetenv("SHOW_CODE").isNull(); - if (showCode) { -#if OS(LINUX) && !defined(Q_OS_ANDROID) - char* disasmOutput = 0; - size_t disasmLength = 0; - FILE* disasmStream = open_memstream(&disasmOutput, &disasmLength); - WTF::setDataFile(disasmStream); -#elif OS(MAC_OS_X) - struct MemStream { - QByteArray buf; - static int write(void *cookie, const char *buf, int len) { - MemStream *stream = reinterpret_cast(cookie); - stream->buf.append(buf, len); - return len; - } - }; - MemStream memStream; - - FILE* disasmStream = fwopen(&memStream, MemStream::write); - WTF::setDataFile(disasmStream); -#endif - - QByteArray name = _function->name->toUtf8(); - if (name.isEmpty()) { - name = QByteArray::number(quintptr(_function), 16); - name.prepend("IR::Function(0x"); - name.append(")"); - } - vmFunc->codeRef = linkBuffer.finalizeCodeWithDisassembly("%s", name.data()); - - WTF::setDataFile(stderr); -#if (OS(LINUX) && !defined(Q_OS_ANDROID)) || OS(MAC_OS_X) - fclose(disasmStream); -# if OS(MAC_OS_X) - char *disasmOutput = memStream.buf.data(); -# endif -# if CPU(X86) || CPU(X86_64) - QHash idents; - printDisassembledOutputWithCalls(disasmOutput, functions, _vmFunction->identifiers); -# endif -# if OS(LINUX) - free(disasmOutput); -# endif -#endif - } else { - vmFunc->codeRef = linkBuffer.finalizeCodeWithoutDisassembly(); - } - - vmFunc->code = (Value (*)(QV4::ExecutionContext *, const uchar *)) vmFunc->codeRef.code().executableAddress(); -} - -InstructionSelection::InstructionSelection(QV4::ExecutionEngine *engine, V4IR::Module *module) - : EvalInstructionSelection(engine, module) - , _block(0) - , _function(0) - , _vmFunction(0) - , _as(0) - , _locals(0) -{ -} - -InstructionSelection::~InstructionSelection() -{ - delete _as; -} - -void InstructionSelection::run(QV4::Function *vmFunction, V4IR::Function *function) -{ - QVector lookups; - QSet reentryBlocks; - qSwap(_function, function); - qSwap(_vmFunction, vmFunction); - qSwap(_lookups, lookups); - qSwap(_reentryBlocks, reentryBlocks); - Assembler* oldAssembler = _as; - _as = new Assembler(_function, _vmFunction, engine()); - - V4IR::Optimizer opt(_function); - opt.run(); - if (opt.isInSSA()) { -#if CPU(X86_64) && (OS(MAC_OS_X) || OS(LINUX)) && 0 - // TODO: add a register allocator here. -#else - // No register allocator available for this platform, so: - opt.convertOutOfSSA(); - ConvertTemps().toStackSlots(_function); -#endif - } else { - ConvertTemps().toStackSlots(_function); - } - - int locals = (_function->tempCount + _function->maxNumberOfArguments) + 1; - locals = (locals + 1) & ~1; - qSwap(_locals, locals); - _as->enterStandardStackFrame(_locals); - - int contextPointer = 0; -#if !defined(RETURN_VALUE_IN_REGISTER) - // When the return VM value doesn't fit into a register, then - // the caller provides a pointer for storage as first argument. - // That shifts the index the context pointer argument by one. - contextPointer++; -#endif - -#ifdef ARGUMENTS_IN_REGISTERS - _as->move(_as->registerForArgument(contextPointer), Assembler::ContextRegister); -#else - _as->loadPtr(addressForArgument(contextPointer), Assembler::ContextRegister); -#endif - - for (int i = 0, ei = _function->basicBlocks.size(); i != ei; ++i) { - V4IR::BasicBlock *nextBlock = (i < ei - 1) ? _function->basicBlocks[i + 1] : 0; - _block = _function->basicBlocks[i]; - _as->registerBlock(_block, nextBlock); - - if (_reentryBlocks.contains(_block)) { - _as->enterStandardStackFrame(/*locals*/0); -#ifdef ARGUMENTS_IN_REGISTERS - _as->move(Assembler::registerForArgument(0), Assembler::ContextRegister); - _as->move(Assembler::registerForArgument(1), Assembler::LocalsRegister); -#else - _as->loadPtr(addressForArgument(0), Assembler::ContextRegister); - _as->loadPtr(addressForArgument(1), Assembler::LocalsRegister); -#endif - } - - foreach (V4IR::Stmt *s, _block->statements) { - if (s->location.isValid()) - _as->recordLineNumber(s->location.startLine); - s->accept(this); - } - } - - _as->link(_vmFunction); - - if (_lookups.size()) { - _vmFunction->lookups = new Lookup[_lookups.size()]; - memcpy(_vmFunction->lookups, _lookups.constData(), _lookups.size()*sizeof(Lookup)); - } - - UnwindHelper::registerFunction(_vmFunction); - - qSwap(_vmFunction, vmFunction); - qSwap(_function, function); - qSwap(_lookups, lookups); - qSwap(_reentryBlocks, reentryBlocks); - qSwap(_locals, locals); - delete _as; - _as = oldAssembler; -} - -void InstructionSelection::callBuiltinInvalid(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result) -{ - int argc = prepareVariableArguments(args); - QV4::String *s = identifier(*func->id); - - if (useFastLookups && func->global) { - uint index = addGlobalLookup(s); - generateFunctionCall(Assembler::Void, __qmljs_call_global_lookup, - Assembler::ContextRegister, Assembler::PointerToValue(result), - Assembler::TrustedImm32(index), - baseAddressForCallArguments(), - Assembler::TrustedImm32(argc)); - } else { - generateFunctionCall(Assembler::Void, __qmljs_call_activation_property, - Assembler::ContextRegister, Assembler::PointerToValue(result), - s, - baseAddressForCallArguments(), - Assembler::TrustedImm32(argc)); - } -} - -void InstructionSelection::callBuiltinTypeofMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) -{ - generateFunctionCall(Assembler::Void, __qmljs_builtin_typeof_member, Assembler::ContextRegister, - Assembler::PointerToValue(result), Assembler::Reference(base), identifier(name)); -} - -void InstructionSelection::callBuiltinTypeofSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) -{ - generateFunctionCall(Assembler::Void, __qmljs_builtin_typeof_element, - Assembler::ContextRegister, Assembler::PointerToValue(result), - Assembler::Reference(base), Assembler::Reference(index)); -} - -void InstructionSelection::callBuiltinTypeofName(const QString &name, V4IR::Temp *result) -{ - generateFunctionCall(Assembler::Void, __qmljs_builtin_typeof_name, Assembler::ContextRegister, Assembler::PointerToValue(result), identifier(name)); -} - -void InstructionSelection::callBuiltinTypeofValue(V4IR::Temp *value, V4IR::Temp *result) -{ - generateFunctionCall(Assembler::Void, __qmljs_builtin_typeof, Assembler::ContextRegister, - Assembler::PointerToValue(result), Assembler::Reference(value)); -} - -void InstructionSelection::callBuiltinDeleteMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) -{ - generateFunctionCall(Assembler::Void, __qmljs_delete_member, Assembler::ContextRegister, - Assembler::PointerToValue(result), Assembler::Reference(base), identifier(name)); -} - -void InstructionSelection::callBuiltinDeleteSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) -{ - generateFunctionCall(Assembler::Void, __qmljs_delete_subscript, Assembler::ContextRegister, - Assembler::PointerToValue(result), Assembler::Reference(base), Assembler::Reference(index)); -} - -void InstructionSelection::callBuiltinDeleteName(const QString &name, V4IR::Temp *result) -{ - generateFunctionCall(Assembler::Void, __qmljs_delete_name, Assembler::ContextRegister, Assembler::PointerToValue(result), identifier(name)); -} - -void InstructionSelection::callBuiltinDeleteValue(V4IR::Temp *result) -{ - _as->storeValue(Value::fromBoolean(false), result); -} - -void InstructionSelection::callBuiltinPostIncrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) -{ - generateFunctionCall(Assembler::Void, __qmljs_builtin_post_increment_member, Assembler::ContextRegister, - Assembler::PointerToValue(result), Assembler::PointerToValue(base), identifier(name)); -} - -void InstructionSelection::callBuiltinPostIncrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) -{ - generateFunctionCall(Assembler::Void, __qmljs_builtin_post_increment_element, Assembler::ContextRegister, - Assembler::PointerToValue(result), Assembler::Reference(base), Assembler::PointerToValue(index)); -} - -void InstructionSelection::callBuiltinPostIncrementName(const QString &name, V4IR::Temp *result) -{ - generateFunctionCall(Assembler::Void, __qmljs_builtin_post_increment_name, Assembler::ContextRegister, - Assembler::PointerToValue(result), identifier(name)); -} - -void InstructionSelection::callBuiltinPostIncrementValue(V4IR::Temp *value, V4IR::Temp *result) -{ - generateFunctionCall(Assembler::Void, __qmljs_builtin_post_increment, - Assembler::PointerToValue(result), Assembler::PointerToValue(value)); -} - -void InstructionSelection::callBuiltinPostDecrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) -{ - generateFunctionCall(Assembler::Void, __qmljs_builtin_post_decrement_member, Assembler::ContextRegister, - Assembler::PointerToValue(result), Assembler::Reference(base), identifier(name)); -} - -void InstructionSelection::callBuiltinPostDecrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) -{ - generateFunctionCall(Assembler::Void, __qmljs_builtin_post_decrement_element, Assembler::ContextRegister, - Assembler::PointerToValue(result), Assembler::Reference(base), - Assembler::Reference(index)); -} - -void InstructionSelection::callBuiltinPostDecrementName(const QString &name, V4IR::Temp *result) -{ - generateFunctionCall(Assembler::Void, __qmljs_builtin_post_decrement_name, Assembler::ContextRegister, - Assembler::PointerToValue(result), identifier(name)); -} - -void InstructionSelection::callBuiltinPostDecrementValue(V4IR::Temp *value, V4IR::Temp *result) -{ - generateFunctionCall(Assembler::Void, __qmljs_builtin_post_decrement, - Assembler::PointerToValue(result), Assembler::PointerToValue(value)); -} - -void InstructionSelection::callBuiltinThrow(V4IR::Temp *arg) -{ - generateFunctionCall(Assembler::Void, __qmljs_throw, Assembler::ContextRegister, Assembler::Reference(arg)); -} - -typedef void *(*MiddleOfFunctionEntryPoint(ExecutionContext *, void *localsPtr)); -static void *tryWrapper(ExecutionContext *context, void *localsPtr, MiddleOfFunctionEntryPoint tryBody, MiddleOfFunctionEntryPoint catchBody, - QV4::String *exceptionVarName, Value *exceptionVar) -{ - *exceptionVar = Value::undefinedValue(); - void *addressToContinueAt = 0; - try { - addressToContinueAt = tryBody(context, localsPtr); - } catch (Exception& ex) { - ex.accept(context); - *exceptionVar = ex.value(); - try { - ExecutionContext *catchContext = __qmljs_builtin_push_catch_scope(exceptionVarName, ex.value(), context); - addressToContinueAt = catchBody(catchContext, localsPtr); - context = __qmljs_builtin_pop_scope(catchContext); - } catch (Exception& ex) { - *exceptionVar = ex.value(); - ex.accept(context); - addressToContinueAt = catchBody(context, localsPtr); - } - } - return addressToContinueAt; -} - -void InstructionSelection::visitTry(V4IR::Try *t) -{ - // Call tryWrapper, which is going to re-enter the same function at the address of the try block. At then end - // of the try function the JIT code will return with the address of the sub-sequent instruction, which tryWrapper - // returns and to which we jump to. - - _reentryBlocks.insert(t->tryBlock); - _reentryBlocks.insert(t->catchBlock); - - generateFunctionCall(Assembler::ReturnValueRegister, tryWrapper, Assembler::ContextRegister, Assembler::LocalsRegister, - Assembler::ReentryBlock(t->tryBlock), Assembler::ReentryBlock(t->catchBlock), - identifier(t->exceptionVarName), Assembler::PointerToValue(t->exceptionVar)); - _as->jump(Assembler::ReturnValueRegister); -} - -void InstructionSelection::callBuiltinFinishTry() -{ - // This assumes that we're in code that was called by tryWrapper, so we return to try wrapper - // with the address that we'd like to continue at, which is right after the ret below. - Assembler::DataLabelPtr continuation = _as->moveWithPatch(Assembler::TrustedImmPtr(0), Assembler::ReturnValueRegister); - _as->leaveStandardStackFrame(/*locals*/0); - _as->ret(); - _as->addPatch(continuation, _as->label()); -} - -void InstructionSelection::callBuiltinForeachIteratorObject(V4IR::Temp *arg, V4IR::Temp *result) -{ - generateFunctionCall(Assembler::Void, __qmljs_foreach_iterator_object, Assembler::ContextRegister, Assembler::PointerToValue(result), Assembler::Reference(arg)); -} - -void InstructionSelection::callBuiltinForeachNextPropertyname(V4IR::Temp *arg, V4IR::Temp *result) -{ - generateFunctionCall(Assembler::Void, __qmljs_foreach_next_property_name, Assembler::PointerToValue(result), Assembler::Reference(arg)); -} - -void InstructionSelection::callBuiltinPushWithScope(V4IR::Temp *arg) -{ - generateFunctionCall(Assembler::ContextRegister, __qmljs_builtin_push_with_scope, Assembler::Reference(arg), Assembler::ContextRegister); -} - -void InstructionSelection::callBuiltinPopScope() -{ - generateFunctionCall(Assembler::ContextRegister, __qmljs_builtin_pop_scope, Assembler::ContextRegister); -} - -void InstructionSelection::callBuiltinDeclareVar(bool deletable, const QString &name) -{ - generateFunctionCall(Assembler::Void, __qmljs_builtin_declare_var, Assembler::ContextRegister, - Assembler::TrustedImm32(deletable), identifier(name)); -} - -void InstructionSelection::callBuiltinDefineGetterSetter(V4IR::Temp *object, const QString &name, V4IR::Temp *getter, V4IR::Temp *setter) -{ - generateFunctionCall(Assembler::Void, __qmljs_builtin_define_getter_setter, Assembler::ContextRegister, - Assembler::Reference(object), identifier(name), Assembler::PointerToValue(getter), Assembler::PointerToValue(setter)); -} - -void InstructionSelection::callBuiltinDefineProperty(V4IR::Temp *object, const QString &name, V4IR::Temp *value) -{ - generateFunctionCall(Assembler::Void, __qmljs_builtin_define_property, Assembler::ContextRegister, - Assembler::Reference(object), identifier(name), Assembler::PointerToValue(value)); -} - -void InstructionSelection::callBuiltinDefineArray(V4IR::Temp *result, V4IR::ExprList *args) -{ - int length = prepareVariableArguments(args); - generateFunctionCall(Assembler::Void, __qmljs_builtin_define_array, Assembler::ContextRegister, - Assembler::PointerToValue(result), - baseAddressForCallArguments(), Assembler::TrustedImm32(length)); -} - -void InstructionSelection::callBuiltinDefineObjectLiteral(V4IR::Temp *result, V4IR::ExprList *args) -{ - int argc = 0; - - InternalClass *klass = engine()->emptyClass; - V4IR::ExprList *it = args; - while (it) { - V4IR::Name *name = it->expr->asName(); - it = it->next; - - bool isData = it->expr->asConst()->value; - it = it->next; - klass = klass->addMember(identifier(*name->id), isData ? QV4::Attr_Data : QV4::Attr_Accessor); - - _as->copyValue(argumentAddressForCall(argc++), it->expr); - - if (!isData) { - it = it->next; - _as->copyValue(argumentAddressForCall(argc++), it->expr); - } - - it = it->next; - } - - generateFunctionCall(Assembler::Void, __qmljs_builtin_define_object_literal, Assembler::ContextRegister, - Assembler::PointerToValue(result), baseAddressForCallArguments(), - Assembler::TrustedImmPtr(klass)); -} - -void InstructionSelection::callValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result) -{ - int argc = prepareVariableArguments(args); - V4IR::Temp* thisObject = 0; - generateFunctionCall(Assembler::Void, __qmljs_call_value, Assembler::ContextRegister, - Assembler::PointerToValue(result), Assembler::PointerToValue(thisObject), - Assembler::Reference(value), baseAddressForCallArguments(), Assembler::TrustedImm32(argc)); -} - -void InstructionSelection::loadThisObject(V4IR::Temp *temp) -{ -#if defined(VALUE_FITS_IN_REGISTER) - _as->load64(Pointer(Assembler::ContextRegister, offsetof(ExecutionContext, thisObject)), Assembler::ReturnValueRegister); - _as->storeReturnValue(temp); -#else - _as->copyValue(temp, Pointer(Assembler::ContextRegister, offsetof(ExecutionContext, thisObject))); -#endif -} - -void InstructionSelection::loadConst(V4IR::Const *sourceConst, V4IR::Temp *targetTemp) -{ - _as->storeValue(convertToValue(sourceConst), targetTemp); -} - -void InstructionSelection::loadString(const QString &str, V4IR::Temp *targetTemp) -{ - Value v = Value::fromString(identifier(str)); - _as->storeValue(v, targetTemp); -} - -void InstructionSelection::loadRegexp(V4IR::RegExp *sourceRegexp, V4IR::Temp *targetTemp) -{ - Value v = Value::fromObject(engine()->newRegExpObject(*sourceRegexp->value, - sourceRegexp->flags)); - _vmFunction->generatedValues.append(v); - _as->storeValue(v, targetTemp); -} - -void InstructionSelection::getActivationProperty(const V4IR::Name *name, V4IR::Temp *temp) -{ - String *propertyName = identifier(*name->id); - if (useFastLookups && name->global) { - uint index = addGlobalLookup(propertyName); - generateLookupCall(index, offsetof(QV4::Lookup, globalGetter), Assembler::ContextRegister, Assembler::PointerToValue(temp)); - return; - } - generateFunctionCall(Assembler::Void, __qmljs_get_activation_property, Assembler::ContextRegister, Assembler::PointerToValue(temp), propertyName); -} - -void InstructionSelection::setActivationProperty(V4IR::Temp *source, const QString &targetName) -{ - String *propertyName = identifier(targetName); - generateFunctionCall(Assembler::Void, __qmljs_set_activation_property, - Assembler::ContextRegister, propertyName, Assembler::Reference(source)); -} - -void InstructionSelection::initClosure(V4IR::Closure *closure, V4IR::Temp *target) -{ - QV4::Function *vmFunc = vmFunction(closure->value); - assert(vmFunc); - generateFunctionCall(Assembler::Void, __qmljs_init_closure, Assembler::ContextRegister, Assembler::PointerToValue(target), Assembler::TrustedImmPtr(vmFunc)); -} - -void InstructionSelection::getProperty(V4IR::Temp *base, const QString &name, V4IR::Temp *target) -{ - if (useFastLookups) { - QV4::String *s = identifier(name); - uint index = addLookup(s); - generateLookupCall(index, offsetof(QV4::Lookup, getter), Assembler::PointerToValue(target), - Assembler::Reference(base)); - } else { - generateFunctionCall(Assembler::Void, __qmljs_get_property, Assembler::ContextRegister, Assembler::PointerToValue(target), - Assembler::Reference(base), identifier(name)); - } -} - -void InstructionSelection::setProperty(V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName) -{ - if (useFastLookups) { - QV4::String *s = identifier(targetName); - uint index = addSetterLookup(s); - generateLookupCall(index, offsetof(QV4::Lookup, setter), Assembler::Reference(targetBase), Assembler::Reference(source)); - } else { - generateFunctionCall(Assembler::Void, __qmljs_set_property, Assembler::ContextRegister, - Assembler::Reference(targetBase), - identifier(targetName), Assembler::Reference(source)); - } -} - -void InstructionSelection::getElement(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *target) -{ - generateFunctionCall(Assembler::Void, __qmljs_get_element, Assembler::ContextRegister, - Assembler::PointerToValue(target), Assembler::Reference(base), - Assembler::Reference(index)); -} - -void InstructionSelection::setElement(V4IR::Temp *source, V4IR::Temp *targetBase, V4IR::Temp *targetIndex) -{ - generateFunctionCall(Assembler::Void, __qmljs_set_element, Assembler::ContextRegister, - Assembler::Reference(targetBase), Assembler::Reference(targetIndex), - Assembler::Reference(source)); -} - -void InstructionSelection::copyValue(V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp) -{ - _as->copyValue(targetTemp, sourceTemp); -} - -#define setOp(op, opName, operation) \ - do { op = operation; opName = isel_stringIfy(operation); } while (0) -#define setOpContext(op, opName, operation) \ - do { opContext = operation; opName = isel_stringIfy(operation); } while (0) - -void InstructionSelection::unop(V4IR::AluOp oper, V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp) -{ - UnaryOpName op = 0; - const char *opName = 0; - switch (oper) { - case V4IR::OpIfTrue: assert(!"unreachable"); break; - case V4IR::OpNot: setOp(op, opName, __qmljs_not); break; - case V4IR::OpUMinus: setOp(op, opName, __qmljs_uminus); break; - case V4IR::OpUPlus: setOp(op, opName, __qmljs_uplus); break; - case V4IR::OpCompl: setOp(op, opName, __qmljs_compl); break; - case V4IR::OpIncrement: setOp(op, opName, __qmljs_increment); break; - case V4IR::OpDecrement: setOp(op, opName, __qmljs_decrement); break; - default: assert(!"unreachable"); break; - } // switch - - if (op) - _as->generateFunctionCallImp(Assembler::Void, opName, op, Assembler::PointerToValue(targetTemp), - Assembler::Reference(sourceTemp)); -} - -void InstructionSelection::binop(V4IR::AluOp oper, V4IR::Expr *leftSource, V4IR::Expr *rightSource, V4IR::Temp *target) -{ - Q_ASSERT(leftSource->asTemp() && rightSource->asTemp()); - _as->generateBinOp(oper, target, leftSource->asTemp(), rightSource->asTemp()); -} - -void InstructionSelection::inplaceNameOp(V4IR::AluOp oper, V4IR::Temp *rightSource, const QString &targetName) -{ - InplaceBinOpName op = 0; - const char *opName = 0; - switch (oper) { - case V4IR::OpBitAnd: setOp(op, opName, __qmljs_inplace_bit_and_name); break; - case V4IR::OpBitOr: setOp(op, opName, __qmljs_inplace_bit_or_name); break; - case V4IR::OpBitXor: setOp(op, opName, __qmljs_inplace_bit_xor_name); break; - case V4IR::OpAdd: setOp(op, opName, __qmljs_inplace_add_name); break; - case V4IR::OpSub: setOp(op, opName, __qmljs_inplace_sub_name); break; - case V4IR::OpMul: setOp(op, opName, __qmljs_inplace_mul_name); break; - case V4IR::OpDiv: setOp(op, opName, __qmljs_inplace_div_name); break; - case V4IR::OpMod: setOp(op, opName, __qmljs_inplace_mod_name); break; - case V4IR::OpLShift: setOp(op, opName, __qmljs_inplace_shl_name); break; - case V4IR::OpRShift: setOp(op, opName, __qmljs_inplace_shr_name); break; - case V4IR::OpURShift: setOp(op, opName, __qmljs_inplace_ushr_name); break; - default: - Q_UNREACHABLE(); - break; - } - if (op) { - _as->generateFunctionCallImp(Assembler::Void, opName, op, Assembler::ContextRegister, - identifier(targetName), Assembler::Reference(rightSource)); - } -} - -void InstructionSelection::inplaceElementOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBaseTemp, V4IR::Temp *targetIndexTemp) -{ - InplaceBinOpElement op = 0; - const char *opName = 0; - switch (oper) { - case V4IR::OpBitAnd: setOp(op, opName, __qmljs_inplace_bit_and_element); break; - case V4IR::OpBitOr: setOp(op, opName, __qmljs_inplace_bit_or_element); break; - case V4IR::OpBitXor: setOp(op, opName, __qmljs_inplace_bit_xor_element); break; - case V4IR::OpAdd: setOp(op, opName, __qmljs_inplace_add_element); break; - case V4IR::OpSub: setOp(op, opName, __qmljs_inplace_sub_element); break; - case V4IR::OpMul: setOp(op, opName, __qmljs_inplace_mul_element); break; - case V4IR::OpDiv: setOp(op, opName, __qmljs_inplace_div_element); break; - case V4IR::OpMod: setOp(op, opName, __qmljs_inplace_mod_element); break; - case V4IR::OpLShift: setOp(op, opName, __qmljs_inplace_shl_element); break; - case V4IR::OpRShift: setOp(op, opName, __qmljs_inplace_shr_element); break; - case V4IR::OpURShift: setOp(op, opName, __qmljs_inplace_ushr_element); break; - default: - Q_UNREACHABLE(); - break; - } - - if (op) { - _as->generateFunctionCallImp(Assembler::Void, opName, op, Assembler::ContextRegister, - Assembler::Reference(targetBaseTemp), Assembler::Reference(targetIndexTemp), - Assembler::Reference(source)); - } -} - -void InstructionSelection::inplaceMemberOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName) -{ - InplaceBinOpMember op = 0; - const char *opName = 0; - switch (oper) { - case V4IR::OpBitAnd: setOp(op, opName, __qmljs_inplace_bit_and_member); break; - case V4IR::OpBitOr: setOp(op, opName, __qmljs_inplace_bit_or_member); break; - case V4IR::OpBitXor: setOp(op, opName, __qmljs_inplace_bit_xor_member); break; - case V4IR::OpAdd: setOp(op, opName, __qmljs_inplace_add_member); break; - case V4IR::OpSub: setOp(op, opName, __qmljs_inplace_sub_member); break; - case V4IR::OpMul: setOp(op, opName, __qmljs_inplace_mul_member); break; - case V4IR::OpDiv: setOp(op, opName, __qmljs_inplace_div_member); break; - case V4IR::OpMod: setOp(op, opName, __qmljs_inplace_mod_member); break; - case V4IR::OpLShift: setOp(op, opName, __qmljs_inplace_shl_member); break; - case V4IR::OpRShift: setOp(op, opName, __qmljs_inplace_shr_member); break; - case V4IR::OpURShift: setOp(op, opName, __qmljs_inplace_ushr_member); break; - default: - Q_UNREACHABLE(); - break; - } - - if (op) { - String* member = identifier(targetName); - _as->generateFunctionCallImp(Assembler::Void, opName, op, Assembler::ContextRegister, - Assembler::Reference(targetBase), identifier(targetName), - Assembler::Reference(source)); - } -} - -void InstructionSelection::callProperty(V4IR::Temp *base, const QString &name, - V4IR::ExprList *args, V4IR::Temp *result) -{ - assert(base != 0); - - int argc = prepareVariableArguments(args); - QV4::String *s = identifier(name); - - if (useFastLookups) { - uint index = addLookup(s); - generateFunctionCall(Assembler::Void, __qmljs_call_property_lookup, - Assembler::ContextRegister, Assembler::PointerToValue(result), - Assembler::Reference(base), Assembler::TrustedImm32(index), - baseAddressForCallArguments(), - Assembler::TrustedImm32(argc)); - } else { - generateFunctionCall(Assembler::Void, __qmljs_call_property, - Assembler::ContextRegister, Assembler::PointerToValue(result), - Assembler::Reference(base), s, - baseAddressForCallArguments(), - Assembler::TrustedImm32(argc)); - } -} - -void InstructionSelection::callSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::ExprList *args, V4IR::Temp *result) -{ - assert(base != 0); - - int argc = prepareVariableArguments(args); - generateFunctionCall(Assembler::Void, __qmljs_call_element, - Assembler::ContextRegister, Assembler::PointerToValue(result), - Assembler::Reference(base), Assembler::Reference(index), - baseAddressForCallArguments(), - Assembler::TrustedImm32(argc)); -} - -void InstructionSelection::convertType(V4IR::Temp *source, V4IR::Temp *target) -{ - // FIXME: do something more useful with this info - copyValue(source, target); -} - -String *InstructionSelection::identifier(const QString &s) -{ - String *str = engine()->newIdentifier(s); - _vmFunction->identifiers.append(str); - return str; -} - -void InstructionSelection::constructActivationProperty(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result) -{ - assert(func != 0); - - if (useFastLookups && func->global) { - int argc = prepareVariableArguments(args); - QV4::String *s = identifier(*func->id); - - uint index = addGlobalLookup(s); - generateFunctionCall(Assembler::Void, __qmljs_construct_global_lookup, - Assembler::ContextRegister, Assembler::PointerToValue(result), - Assembler::TrustedImm32(index), - baseAddressForCallArguments(), - Assembler::TrustedImm32(argc)); - return; - } - - callRuntimeMethod(result, __qmljs_construct_activation_property, func, args); -} - -void InstructionSelection::constructProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result) -{ - int argc = prepareVariableArguments(args); - generateFunctionCall(Assembler::Void, __qmljs_construct_property, Assembler::ContextRegister, - Assembler::PointerToValue(result), Assembler::Reference(base), identifier(name), baseAddressForCallArguments(), Assembler::TrustedImm32(argc)); -} - -void InstructionSelection::constructValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result) -{ - assert(value != 0); - - int argc = prepareVariableArguments(args); - generateFunctionCall(Assembler::Void, __qmljs_construct_value, Assembler::ContextRegister, - Assembler::PointerToValue(result), Assembler::Reference(value), baseAddressForCallArguments(), Assembler::TrustedImm32(argc)); -} - -void InstructionSelection::visitJump(V4IR::Jump *s) -{ - _as->jumpToBlock(_block, s->target); -} - -void InstructionSelection::visitCJump(V4IR::CJump *s) -{ - if (V4IR::Temp *t = s->cond->asTemp()) { - Address temp = _as->loadTempAddress(Assembler::ScratchRegister, t); - Address tag = temp; - tag.offset += offsetof(QV4::Value, tag); - Assembler::Jump booleanConversion = _as->branch32(Assembler::NotEqual, tag, Assembler::TrustedImm32(QV4::Value::Boolean_Type)); - - Address data = temp; - data.offset += offsetof(QV4::Value, int_32); - _as->load32(data, Assembler::ReturnValueRegister); - Assembler::Jump testBoolean = _as->jump(); - - booleanConversion.link(_as); - { - generateFunctionCall(Assembler::ReturnValueRegister, __qmljs_to_boolean, Assembler::Reference(t)); - } - - testBoolean.link(_as); - Assembler::Jump target = _as->branch32(Assembler::NotEqual, Assembler::ReturnValueRegister, Assembler::TrustedImm32(0)); - _as->addPatch(s->iftrue, target); - - _as->jumpToBlock(_block, s->iffalse); - return; - } else if (V4IR::Binop *b = s->cond->asBinop()) { - if (b->left->asTemp() && b->right->asTemp()) { - CmpOp op = 0; - CmpOpContext opContext = 0; - const char *opName = 0; - switch (b->op) { - default: Q_UNREACHABLE(); assert(!"todo"); break; - case V4IR::OpGt: setOp(op, opName, __qmljs_cmp_gt); break; - case V4IR::OpLt: setOp(op, opName, __qmljs_cmp_lt); break; - case V4IR::OpGe: setOp(op, opName, __qmljs_cmp_ge); break; - case V4IR::OpLe: setOp(op, opName, __qmljs_cmp_le); break; - case V4IR::OpEqual: setOp(op, opName, __qmljs_cmp_eq); break; - case V4IR::OpNotEqual: setOp(op, opName, __qmljs_cmp_ne); break; - case V4IR::OpStrictEqual: setOp(op, opName, __qmljs_cmp_se); break; - case V4IR::OpStrictNotEqual: setOp(op, opName, __qmljs_cmp_sne); break; - case V4IR::OpInstanceof: setOpContext(op, opName, __qmljs_cmp_instanceof); break; - case V4IR::OpIn: setOpContext(op, opName, __qmljs_cmp_in); break; - } // switch - - if (opContext) - _as->generateFunctionCallImp(Assembler::ReturnValueRegister, opName, opContext, Assembler::ContextRegister, - Assembler::Reference(b->left->asTemp()), - Assembler::Reference(b->right->asTemp())); - else - _as->generateFunctionCallImp(Assembler::ReturnValueRegister, opName, op, - Assembler::Reference(b->left->asTemp()), - Assembler::Reference(b->right->asTemp())); - - Assembler::Jump target = _as->branch32(Assembler::NotEqual, Assembler::ReturnValueRegister, Assembler::TrustedImm32(0)); - _as->addPatch(s->iftrue, target); - - _as->jumpToBlock(_block, s->iffalse); - return; - } else { - assert(!"wip"); - } - Q_UNIMPLEMENTED(); - } - Q_UNIMPLEMENTED(); - assert(!"TODO"); -} - -void InstructionSelection::visitRet(V4IR::Ret *s) -{ - if (V4IR::Temp *t = s->expr->asTemp()) { -#if defined(RETURN_VALUE_IN_REGISTER) -#if CPU(X86) - Address addr = _as->loadTempAddress(Assembler::ScratchRegister, t); - _as->load32(addr, JSC::X86Registers::eax); - addr.offset += 4; - _as->load32(addr, JSC::X86Registers::edx); -#else - _as->copyValue(Assembler::ReturnValueRegister, t); -#endif -#else - _as->loadPtr(addressForArgument(0), Assembler::ReturnValueRegister); - _as->copyValue(Address(Assembler::ReturnValueRegister, 0), t); -#endif - } else if (V4IR::Const *c = s->expr->asConst()) { - _as->copyValue(Assembler::ReturnValueRegister, c); - } else { - Q_UNIMPLEMENTED(); - Q_UNREACHABLE(); - Q_UNUSED(s); - } - - _as->leaveStandardStackFrame(_locals); -#if !defined(ARGUMENTS_IN_REGISTERS) && !defined(RETURN_VALUE_IN_REGISTER) - // Emulate ret(n) instruction - // Pop off return address into scratch register ... - _as->pop(Assembler::ScratchRegister); - // ... and overwrite the invisible argument with - // the return address. - _as->poke(Assembler::ScratchRegister); -#endif - _as->ret(); -} - -int InstructionSelection::prepareVariableArguments(V4IR::ExprList* args) -{ - int argc = 0; - for (V4IR::ExprList *it = args; it; it = it->next) { - ++argc; - } - - int i = 0; - for (V4IR::ExprList *it = args; it; it = it->next, ++i) { -// V4IR::Temp *arg = it->expr->asTemp(); -// assert(arg != 0); - _as->copyValue(argumentAddressForCall(i), it->expr); - } - - return argc; -} - -void InstructionSelection::callRuntimeMethodImp(V4IR::Temp *result, const char* name, ActivationMethod method, V4IR::Expr *base, V4IR::ExprList *args) -{ - V4IR::Name *baseName = base->asName(); - assert(baseName != 0); - - int argc = prepareVariableArguments(args); - _as->generateFunctionCallImp(Assembler::Void, name, method, Assembler::ContextRegister, Assembler::PointerToValue(result), - identifier(*baseName->id), baseAddressForCallArguments(), - Assembler::TrustedImm32(argc)); -} - - -uint InstructionSelection::addLookup(QV4::String *name) -{ - uint index = (uint)_lookups.size(); - QV4::Lookup l; - l.getter = Lookup::getterGeneric; - for (int i = 0; i < Lookup::Size; ++i) - l.classList[i] = 0; - l.level = -1; - l.index = UINT_MAX; - l.name = name; - _lookups.append(l); - return index; -} - -uint InstructionSelection::addSetterLookup(QV4::String *name) -{ - uint index = (uint)_lookups.size(); - QV4::Lookup l; - l.setter = Lookup::setterGeneric; - for (int i = 0; i < Lookup::Size; ++i) - l.classList[i] = 0; - l.level = -1; - l.index = UINT_MAX; - l.name = name; - _lookups.append(l); - return index; -} - -uint InstructionSelection::addGlobalLookup(QV4::String *name) -{ - uint index = (uint)_lookups.size(); - QV4::Lookup l; - l.globalGetter = Lookup::globalGetterGeneric; - for (int i = 0; i < Lookup::Size; ++i) - l.classList[i] = 0; - l.level = -1; - l.index = UINT_MAX; - l.name = name; - _lookups.append(l); - return index; -} diff --git a/src/qml/qml/v4/qv4isel_masm_p.h b/src/qml/qml/v4/qv4isel_masm_p.h deleted file mode 100644 index fa14ffac62..0000000000 --- a/src/qml/qml/v4/qv4isel_masm_p.h +++ /dev/null @@ -1,939 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QV4ISEL_MASM_P_H -#define QV4ISEL_MASM_P_H - -#include "qv4global_p.h" -#include "qv4jsir_p.h" -#include "qv4isel_p.h" -#include "qv4isel_util_p.h" -#include "qv4object_p.h" -#include "qv4runtime_p.h" -#include "qv4lookup_p.h" - -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE - -namespace QQmlJS { -namespace MASM { - -class Assembler : public JSC::MacroAssembler -{ -public: - Assembler(V4IR::Function* function, QV4::Function *vmFunction, QV4::ExecutionEngine *engine); -#if CPU(X86) - -#undef VALUE_FITS_IN_REGISTER -#undef ARGUMENTS_IN_REGISTERS - -#if OS(WINDOWS) - // Returned in EAX:EDX pair -#define RETURN_VALUE_IN_REGISTER -#else -#undef RETURN_VALUE_IN_REGISTER -#endif - -#define HAVE_ALU_OPS_WITH_MEM_OPERAND 1 - - static const RegisterID StackFrameRegister = JSC::X86Registers::ebp; - static const RegisterID StackPointerRegister = JSC::X86Registers::esp; - static const RegisterID LocalsRegister = JSC::X86Registers::edi; - static const RegisterID ContextRegister = JSC::X86Registers::esi; - static const RegisterID ReturnValueRegister = JSC::X86Registers::eax; - static const RegisterID ScratchRegister = JSC::X86Registers::ecx; - static const RegisterID IntegerOpRegister = JSC::X86Registers::eax; - static const FPRegisterID FPGpr0 = JSC::X86Registers::xmm0; - - static const int RegisterSize = 4; - - static const int RegisterArgumentCount = 0; - static RegisterID registerForArgument(int) - { - assert(false); - // Not reached. - return JSC::X86Registers::eax; - } - - // Return address is pushed onto stack by the CPU. - static const int StackSpaceAllocatedUponFunctionEntry = RegisterSize; - static const int StackShadowSpace = 0; - inline void platformEnterStandardStackFrame() {} - inline void platformLeaveStandardStackFrame() {} -#elif CPU(X86_64) - -#define VALUE_FITS_IN_REGISTER -#define ARGUMENTS_IN_REGISTERS -#define RETURN_VALUE_IN_REGISTER -#define HAVE_ALU_OPS_WITH_MEM_OPERAND 1 - - static const RegisterID StackFrameRegister = JSC::X86Registers::ebp; - static const RegisterID StackPointerRegister = JSC::X86Registers::esp; - static const RegisterID LocalsRegister = JSC::X86Registers::r12; - static const RegisterID ContextRegister = JSC::X86Registers::r14; - static const RegisterID ReturnValueRegister = JSC::X86Registers::eax; - static const RegisterID ScratchRegister = JSC::X86Registers::r10; - static const RegisterID IntegerOpRegister = JSC::X86Registers::eax; - static const FPRegisterID FPGpr0 = JSC::X86Registers::xmm0; - - static const int RegisterSize = 8; - -#if OS(WINDOWS) - static const int RegisterArgumentCount = 4; - static RegisterID registerForArgument(int index) - { - static RegisterID regs[RegisterArgumentCount] = { - JSC::X86Registers::ecx, - JSC::X86Registers::edx, - JSC::X86Registers::r8, - JSC::X86Registers::r9 - }; - assert(index >= 0 && index < RegisterArgumentCount); - return regs[index]; - }; - static const int StackShadowSpace = 32; -#else // Unix - static const int RegisterArgumentCount = 6; - static RegisterID registerForArgument(int index) - { - static RegisterID regs[RegisterArgumentCount] = { - JSC::X86Registers::edi, - JSC::X86Registers::esi, - JSC::X86Registers::edx, - JSC::X86Registers::ecx, - JSC::X86Registers::r8, - JSC::X86Registers::r9 - }; - assert(index >= 0 && index < RegisterArgumentCount); - return regs[index]; - }; - static const int StackShadowSpace = 0; -#endif - - // Return address is pushed onto stack by the CPU. - static const int StackSpaceAllocatedUponFunctionEntry = RegisterSize; - inline void platformEnterStandardStackFrame() {} - inline void platformLeaveStandardStackFrame() {} -#elif CPU(ARM) - -#undef VALUE_FITS_IN_REGISTER -#define ARGUMENTS_IN_REGISTERS -#undef RETURN_VALUE_IN_REGISTER -#undef HAVE_ALU_OPS_WITH_MEM_OPERAND - - static const RegisterID StackFrameRegister = JSC::ARMRegisters::r4; - static const RegisterID StackPointerRegister = JSC::ARMRegisters::sp; - static const RegisterID LocalsRegister = JSC::ARMRegisters::r7; - static const RegisterID ContextRegister = JSC::ARMRegisters::r5; - static const RegisterID ReturnValueRegister = JSC::ARMRegisters::r0; - static const RegisterID ScratchRegister = JSC::ARMRegisters::r6; - static const RegisterID IntegerOpRegister = JSC::ARMRegisters::r0; - static const FPRegisterID FPGpr0 = JSC::ARMRegisters::d0; - - static const int RegisterSize = 4; - - static const RegisterID RegisterArgument1 = JSC::ARMRegisters::r0; - static const RegisterID RegisterArgument2 = JSC::ARMRegisters::r1; - static const RegisterID RegisterArgument3 = JSC::ARMRegisters::r2; - static const RegisterID RegisterArgument4 = JSC::ARMRegisters::r3; - - static const int RegisterArgumentCount = 4; - static RegisterID registerForArgument(int index) - { - assert(index >= 0 && index < RegisterArgumentCount); - return static_cast(JSC::ARMRegisters::r0 + index); - }; - - // Registers saved in platformEnterStandardStackFrame below. - static const int StackSpaceAllocatedUponFunctionEntry = 5 * RegisterSize; - static const int StackShadowSpace = 0; - inline void platformEnterStandardStackFrame() - { - // Move the register arguments onto the stack as if they were - // pushed by the caller, just like on ia32. This gives us consistent - // access to the parameters if we need to. - push(JSC::ARMRegisters::r3); - push(JSC::ARMRegisters::r2); - push(JSC::ARMRegisters::r1); - push(JSC::ARMRegisters::r0); - push(JSC::ARMRegisters::lr); - } - inline void platformLeaveStandardStackFrame() - { - pop(JSC::ARMRegisters::lr); - addPtr(TrustedImm32(4 * RegisterSize), StackPointerRegister); - } -#else -#error The JIT needs to be ported to this platform. -#endif - static const int calleeSavedRegisterCount; - -#if CPU(X86) || CPU(X86_64) - static const int StackAlignment = 16; -#elif CPU(ARM) - // Per AAPCS - static const int StackAlignment = 8; -#else -#error Stack alignment unknown for this platform. -#endif - - // Explicit type to allow distinguishing between - // pushing an address itself or the value it points - // to onto the stack when calling functions. - struct Pointer : public Address - { - explicit Pointer(const Address& addr) - : Address(addr) - {} - explicit Pointer(RegisterID reg, int32_t offset) - : Address(reg, offset) - {} - }; - - struct VoidType { VoidType() {} }; - static const VoidType Void; - - - typedef JSC::FunctionPtr FunctionPtr; - - struct CallToLink { - Call call; - FunctionPtr externalFunction; - const char* functionName; - }; - struct PointerToValue { - PointerToValue(V4IR::Temp *value) : value(value) {} - V4IR::Temp *value; - }; - struct Reference { - Reference(V4IR::Temp *value) : value(value) {} - V4IR::Temp *value; - }; - - struct ReentryBlock { - ReentryBlock(V4IR::BasicBlock *b) : block(b) {} - V4IR::BasicBlock *block; - }; - - void callAbsolute(const char* functionName, FunctionPtr function) { - CallToLink ctl; - ctl.call = call(); - ctl.externalFunction = function; - ctl.functionName = functionName; - _callsToLink.append(ctl); - } - - void callAbsolute(const char* /*functionName*/, Address addr) { - call(addr); - } - - void registerBlock(V4IR::BasicBlock*, V4IR::BasicBlock *nextBlock); - void jumpToBlock(V4IR::BasicBlock* current, V4IR::BasicBlock *target); - void addPatch(V4IR::BasicBlock* targetBlock, Jump targetJump); - void addPatch(DataLabelPtr patch, Label target); - void addPatch(DataLabelPtr patch, V4IR::BasicBlock *target); - - Pointer loadTempAddress(RegisterID reg, V4IR::Temp *t); - - void loadArgumentInRegister(RegisterID source, RegisterID dest) - { - move(source, dest); - } - - void loadArgumentInRegister(TrustedImmPtr ptr, RegisterID dest) - { - move(TrustedImmPtr(ptr), dest); - } - - void loadArgumentInRegister(const Pointer& ptr, RegisterID dest) - { - addPtr(TrustedImm32(ptr.offset), ptr.base, dest); - } - - void loadArgumentInRegister(PointerToValue temp, RegisterID dest) - { - if (!temp.value) { - loadArgumentInRegister(TrustedImmPtr(0), dest); - } else { - Pointer addr = loadTempAddress(dest, temp.value); - loadArgumentInRegister(addr, dest); - } - } - - void loadArgumentInRegister(Reference temp, RegisterID dest) - { - assert(temp.value); - Pointer addr = loadTempAddress(dest, temp.value); - loadArgumentInRegister(addr, dest); - } - - void loadArgumentInRegister(ReentryBlock block, RegisterID dest) - { - assert(block.block); - DataLabelPtr patch = moveWithPatch(TrustedImmPtr(0), dest); - addPatch(patch, block.block); - } - -#ifdef VALUE_FITS_IN_REGISTER - void loadArgumentInRegister(V4IR::Temp* temp, RegisterID dest) - { - if (!temp) { - QV4::Value undefined = QV4::Value::undefinedValue(); - move(TrustedImm64(undefined.val), dest); - } else { - Pointer addr = loadTempAddress(dest, temp); - load64(addr, dest); - } - } - - void loadArgumentInRegister(V4IR::Const* c, RegisterID dest) - { - QV4::Value v = convertToValue(c); - move(TrustedImm64(v.val), dest); - } - - void loadArgumentInRegister(V4IR::Expr* expr, RegisterID dest) - { - if (!expr) { - QV4::Value undefined = QV4::Value::undefinedValue(); - move(TrustedImm64(undefined.val), dest); - } else if (expr->asTemp()){ - loadArgumentInRegister(expr->asTemp(), dest); - } else if (expr->asConst()) { - loadArgumentInRegister(expr->asConst(), dest); - } else { - assert(!"unimplemented expression type in loadArgument"); - } - } -#else - void loadArgumentInRegister(V4IR::Expr*, RegisterID) - { - assert(!"unimplemented: expression in loadArgument"); - } -#endif - - void loadArgumentInRegister(QV4::String* string, RegisterID dest) - { - loadArgumentInRegister(TrustedImmPtr(string), dest); - } - - void loadArgumentInRegister(TrustedImm32 imm32, RegisterID dest) - { - xorPtr(dest, dest); - if (imm32.m_value) - move(imm32, dest); - } - - void storeReturnValue(RegisterID dest) - { - move(ReturnValueRegister, dest); - } - -#ifdef VALUE_FITS_IN_REGISTER - void storeReturnValue(const Pointer &dest) - { - store64(ReturnValueRegister, dest); - } - - void storeReturnValue(V4IR::Temp *temp) - { - if (!temp) - return; - Pointer addr = loadTempAddress(ScratchRegister, temp); - storeReturnValue(addr); - } -#endif - - void storeReturnValue(VoidType) - { - } - - template - void loadArgumentOnStack(RegisterID reg) - { - poke(reg, StackSlot); - } - - template - void loadArgumentOnStack(TrustedImm32 value) - { - poke(value, StackSlot); - } - - template - void loadArgumentOnStack(const Pointer& ptr) - { - addPtr(TrustedImm32(ptr.offset), ptr.base, ScratchRegister); - poke(ScratchRegister, StackSlot); - } - - template - void loadArgumentOnStack(PointerToValue temp) - { - if (temp.value) { - Pointer ptr = loadTempAddress(ScratchRegister, temp.value); - loadArgumentOnStack(ptr); - } else { - poke(TrustedImmPtr(0), StackSlot); - } - } - - template - void loadArgumentOnStack(Reference temp) - { - assert (temp.value); - - Pointer ptr = loadTempAddress(ScratchRegister, temp.value); - loadArgumentOnStack(ptr); - } - - template - void loadArgumentOnStack(ReentryBlock block) - { - assert(block.block); - DataLabelPtr patch = moveWithPatch(TrustedImmPtr(0), ScratchRegister); - poke(ScratchRegister, StackSlot); - addPatch(patch, block.block); - } - - template - void loadArgumentOnStack(TrustedImmPtr ptr) - { - move(TrustedImmPtr(ptr), ScratchRegister); - poke(ScratchRegister, StackSlot); - } - - template - void loadArgumentOnStack(QV4::String* name) - { - poke(TrustedImmPtr(name), StackSlot); - } - - using JSC::MacroAssembler::loadDouble; - void loadDouble(V4IR::Temp* temp, FPRegisterID dest) - { - Pointer ptr = loadTempAddress(ScratchRegister, temp); - loadDouble(ptr, dest); - } - - using JSC::MacroAssembler::storeDouble; - void storeDouble(FPRegisterID source, V4IR::Temp* temp) - { - Pointer ptr = loadTempAddress(ScratchRegister, temp); - storeDouble(source, ptr); - } - - template - void copyValue(Result result, Source source); - template - void copyValue(Result result, V4IR::Expr* source); - - void storeValue(QV4::Value value, Address destination) - { -#ifdef VALUE_FITS_IN_REGISTER - store64(TrustedImm64(value.val), destination); -#else - store32(TrustedImm32(value.int_32), destination); - destination.offset += 4; - store32(TrustedImm32(value.tag), destination); -#endif - } - - void storeValue(QV4::Value value, V4IR::Temp* temp); - - static int calculateStackFrameSize(int locals); - void enterStandardStackFrame(int locals); - void leaveStandardStackFrame(int locals); - - template - void loadArgumentOnStackOrRegister(const T &value) - { - if (argumentNumber < RegisterArgumentCount) - loadArgumentInRegister(value, registerForArgument(argumentNumber)); - else -#if OS(WINDOWS) && CPU(X86_64) - loadArgumentOnStack(value); -#else // Sanity: - loadArgumentOnStack(value); -#endif - } - - template - void loadArgumentOnStackOrRegister(const VoidType &value) - { - Q_UNUSED(value); - } - - template - struct Select - { - enum { Chosen = First }; - }; - - template - struct Select - { - enum { Chosen = Second }; - }; - - template - struct SizeOnStack - { - enum { Size = Select= RegisterArgumentCount, QT_POINTER_SIZE, 0>::Chosen }; - }; - - template - struct SizeOnStack - { - enum { Size = 0 }; - }; - - - template - void generateFunctionCallImp(ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5, Arg6 arg6) - { - int stackSpaceNeeded = SizeOnStack<0, Arg1>::Size - + SizeOnStack<1, Arg2>::Size - + SizeOnStack<2, Arg3>::Size - + SizeOnStack<3, Arg4>::Size - + SizeOnStack<4, Arg5>::Size - + SizeOnStack<5, Arg6>::Size - + StackShadowSpace; - - if (stackSpaceNeeded) { - stackSpaceNeeded = WTF::roundUpToMultipleOf(StackAlignment, stackSpaceNeeded); - sub32(TrustedImm32(stackSpaceNeeded), StackPointerRegister); - } - - loadArgumentOnStackOrRegister<5>(arg6); - loadArgumentOnStackOrRegister<4>(arg5); - loadArgumentOnStackOrRegister<3>(arg4); - loadArgumentOnStackOrRegister<2>(arg3); - loadArgumentOnStackOrRegister<1>(arg2); - loadArgumentOnStackOrRegister<0>(arg1); - - callAbsolute(functionName, function); - - storeReturnValue(r); - - if (stackSpaceNeeded) - add32(TrustedImm32(stackSpaceNeeded), StackPointerRegister); - } - - template - void generateFunctionCallImp(ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5) - { - generateFunctionCallImp(r, functionName, function, arg1, arg2, arg3, arg4, arg5, VoidType()); - } - - template - void generateFunctionCallImp(ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4) - { - generateFunctionCallImp(r, functionName, function, arg1, arg2, arg3, arg4, VoidType()); - } - - template - void generateFunctionCallImp(ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2, Arg3 arg3) - { - generateFunctionCallImp(r, functionName, function, arg1, arg2, arg3, VoidType(), VoidType()); - } - - template - void generateFunctionCallImp(ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2) - { - generateFunctionCallImp(r, functionName, function, arg1, arg2, VoidType(), VoidType(), VoidType()); - } - - template - void generateFunctionCallImp(ArgRet r, const char* functionName, Callable function, Arg1 arg1) - { - generateFunctionCallImp(r, functionName, function, arg1, VoidType(), VoidType(), VoidType(), VoidType()); - } - - typedef Jump (Assembler::*MemRegBinOp)(Address, RegisterID); - typedef Jump (Assembler::*ImmRegBinOp)(TrustedImm32, RegisterID); - - struct BinaryOperationInfo { - const char *name; - QV4::BinOp fallbackImplementation; - QV4::BinOpContext contextImplementation; - MemRegBinOp inlineMemRegOp; - ImmRegBinOp inlineImmRegOp; - }; - - static const BinaryOperationInfo binaryOperations[QQmlJS::V4IR::LastAluOp + 1]; - - void generateBinOp(V4IR::AluOp operation, V4IR::Temp* target, V4IR::Temp* left, V4IR::Temp* right); - - Jump inline_add32(Address addr, RegisterID reg) - { -#if HAVE(ALU_OPS_WITH_MEM_OPERAND) - return branchAdd32(Overflow, addr, reg); -#else - load32(addr, ScratchRegister); - return branchAdd32(Overflow, ScratchRegister, reg); -#endif - } - - Jump inline_add32(TrustedImm32 imm, RegisterID reg) - { - return branchAdd32(Overflow, imm, reg); - } - - Jump inline_sub32(Address addr, RegisterID reg) - { -#if HAVE(ALU_OPS_WITH_MEM_OPERAND) - return branchSub32(Overflow, addr, reg); -#else - load32(addr, ScratchRegister); - return branchSub32(Overflow, ScratchRegister, reg); -#endif - } - - Jump inline_sub32(TrustedImm32 imm, RegisterID reg) - { - return branchSub32(Overflow, imm, reg); - } - - Jump inline_mul32(Address addr, RegisterID reg) - { -#if HAVE(ALU_OPS_WITH_MEM_OPERAND) - return branchMul32(Overflow, addr, reg); -#else - load32(addr, ScratchRegister); - return branchMul32(Overflow, ScratchRegister, reg); -#endif - } - - Jump inline_mul32(TrustedImm32 imm, RegisterID reg) - { - return branchMul32(Overflow, imm, reg, reg); - } - - Jump inline_shl32(Address addr, RegisterID reg) - { - load32(addr, ScratchRegister); - and32(TrustedImm32(0x1f), ScratchRegister); - lshift32(ScratchRegister, reg); - return Jump(); - } - - Jump inline_shl32(TrustedImm32 imm, RegisterID reg) - { - imm.m_value &= 0x1f; - lshift32(imm, reg); - return Jump(); - } - - Jump inline_shr32(Address addr, RegisterID reg) - { - load32(addr, ScratchRegister); - and32(TrustedImm32(0x1f), ScratchRegister); - rshift32(ScratchRegister, reg); - return Jump(); - } - - Jump inline_shr32(TrustedImm32 imm, RegisterID reg) - { - imm.m_value &= 0x1f; - rshift32(imm, reg); - return Jump(); - } - - Jump inline_ushr32(Address addr, RegisterID reg) - { - load32(addr, ScratchRegister); - and32(TrustedImm32(0x1f), ScratchRegister); - urshift32(ScratchRegister, reg); - return branchTest32(Signed, reg, reg); - } - - Jump inline_ushr32(TrustedImm32 imm, RegisterID reg) - { - imm.m_value &= 0x1f; - urshift32(imm, reg); - return branchTest32(Signed, reg, reg); - } - - Jump inline_and32(Address addr, RegisterID reg) - { -#if HAVE(ALU_OPS_WITH_MEM_OPERAND) - and32(addr, reg); -#else - load32(addr, ScratchRegister); - and32(ScratchRegister, reg); -#endif - return Jump(); - } - - Jump inline_and32(TrustedImm32 imm, RegisterID reg) - { - and32(imm, reg); - return Jump(); - } - - Jump inline_or32(Address addr, RegisterID reg) - { -#if HAVE(ALU_OPS_WITH_MEM_OPERAND) - or32(addr, reg); -#else - load32(addr, ScratchRegister); - or32(ScratchRegister, reg); -#endif - return Jump(); - } - - Jump inline_or32(TrustedImm32 imm, RegisterID reg) - { - or32(imm, reg); - return Jump(); - } - - Jump inline_xor32(Address addr, RegisterID reg) - { -#if HAVE(ALU_OPS_WITH_MEM_OPERAND) - xor32(addr, reg); -#else - load32(addr, ScratchRegister); - xor32(ScratchRegister, reg); -#endif - return Jump(); - } - - Jump inline_xor32(TrustedImm32 imm, RegisterID reg) - { - xor32(imm, reg); - return Jump(); - } - - void link(QV4::Function *vmFunc); - - void recordLineNumber(int lineNumber); - -private: - V4IR::Function *_function; - QV4::Function *_vmFunction; - QHash _addrs; - QHash > _patches; - QList _callsToLink; - - struct DataLabelPatch { - DataLabelPtr dataLabel; - Label target; - }; - QList _dataLabelPatches; - - QHash > _labelPatches; - V4IR::BasicBlock *_nextBlock; - - QV4::ExecutionEngine *_engine; - - struct CodeLineNumerMapping - { - Assembler::Label location; - int lineNumber; - }; - QVector codeLineNumberMappings; -}; - -class Q_QML_EXPORT InstructionSelection: - protected V4IR::IRDecoder, - public EvalInstructionSelection -{ -public: - InstructionSelection(QV4::ExecutionEngine *engine, V4IR::Module *module); - ~InstructionSelection(); - - virtual void run(QV4::Function *vmFunction, V4IR::Function *function); - -protected: - virtual void callBuiltinInvalid(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result); - virtual void callBuiltinTypeofMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result); - virtual void callBuiltinTypeofSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result); - virtual void callBuiltinTypeofName(const QString &name, V4IR::Temp *result); - virtual void callBuiltinTypeofValue(V4IR::Temp *value, V4IR::Temp *result); - virtual void callBuiltinDeleteMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result); - virtual void callBuiltinDeleteSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result); - virtual void callBuiltinDeleteName(const QString &name, V4IR::Temp *result); - virtual void callBuiltinDeleteValue(V4IR::Temp *result); - virtual void callBuiltinPostDecrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result); - virtual void callBuiltinPostDecrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result); - virtual void callBuiltinPostDecrementName(const QString &name, V4IR::Temp *result); - virtual void callBuiltinPostDecrementValue(V4IR::Temp *value, V4IR::Temp *result); - virtual void callBuiltinPostIncrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result); - virtual void callBuiltinPostIncrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result); - virtual void callBuiltinPostIncrementName(const QString &name, V4IR::Temp *result); - virtual void callBuiltinPostIncrementValue(V4IR::Temp *value, V4IR::Temp *result); - virtual void callBuiltinThrow(V4IR::Temp *arg); - virtual void callBuiltinFinishTry(); - virtual void callBuiltinForeachIteratorObject(V4IR::Temp *arg, V4IR::Temp *result); - virtual void callBuiltinForeachNextPropertyname(V4IR::Temp *arg, V4IR::Temp *result); - virtual void callBuiltinPushWithScope(V4IR::Temp *arg); - virtual void callBuiltinPopScope(); - virtual void callBuiltinDeclareVar(bool deletable, const QString &name); - virtual void callBuiltinDefineGetterSetter(V4IR::Temp *object, const QString &name, V4IR::Temp *getter, V4IR::Temp *setter); - virtual void callBuiltinDefineProperty(V4IR::Temp *object, const QString &name, V4IR::Temp *value); - virtual void callBuiltinDefineArray(V4IR::Temp *result, V4IR::ExprList *args); - virtual void callBuiltinDefineObjectLiteral(V4IR::Temp *result, V4IR::ExprList *args); - virtual void callValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result); - virtual void callProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result); - virtual void callSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::ExprList *args, V4IR::Temp *result); - virtual void convertType(V4IR::Temp *source, V4IR::Temp *target); - virtual void loadThisObject(V4IR::Temp *temp); - virtual void loadConst(V4IR::Const *sourceConst, V4IR::Temp *targetTemp); - virtual void loadString(const QString &str, V4IR::Temp *targetTemp); - virtual void loadRegexp(V4IR::RegExp *sourceRegexp, V4IR::Temp *targetTemp); - virtual void getActivationProperty(const V4IR::Name *name, V4IR::Temp *temp); - virtual void setActivationProperty(V4IR::Temp *source, const QString &targetName); - virtual void initClosure(V4IR::Closure *closure, V4IR::Temp *target); - virtual void getProperty(V4IR::Temp *base, const QString &name, V4IR::Temp *target); - virtual void setProperty(V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName); - virtual void getElement(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *target); - virtual void setElement(V4IR::Temp *source, V4IR::Temp *targetBase, V4IR::Temp *targetIndex); - virtual void copyValue(V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp); - virtual void unop(V4IR::AluOp oper, V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp); - virtual void binop(V4IR::AluOp oper, V4IR::Expr *leftSource, V4IR::Expr *rightSource, V4IR::Temp *target); - virtual void inplaceNameOp(V4IR::AluOp oper, V4IR::Temp *rightSource, const QString &targetName); - virtual void inplaceElementOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBaseTemp, V4IR::Temp *targetIndexTemp); - virtual void inplaceMemberOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName); - - typedef Assembler::Address Address; - typedef Assembler::Pointer Pointer; - - Address addressForArgument(int index) const - { - // StackFrameRegister points to its old value on the stack, and above - // it we have the return address, hence the need to step over two - // values before reaching the first argument. - return Address(Assembler::StackFrameRegister, (index + 2) * sizeof(void*)); - } - - // Some run-time functions take (Value* args, int argc). This function is for populating - // the args. - Pointer argumentAddressForCall(int argument) - { - const int index = _function->maxNumberOfArguments - argument; - return Pointer(Assembler::LocalsRegister, sizeof(QV4::Value) * (-index) - - sizeof(void*) // size of ebp - - sizeof(void*) * Assembler::calleeSavedRegisterCount - ); - } - Pointer baseAddressForCallArguments() - { - return argumentAddressForCall(0); - } - - QV4::String *identifier(const QString &s); - virtual void constructActivationProperty(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result); - virtual void constructProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result); - virtual void constructValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result); - - virtual void visitJump(V4IR::Jump *); - virtual void visitCJump(V4IR::CJump *); - virtual void visitRet(V4IR::Ret *); - virtual void visitTry(V4IR::Try *); - -private: - #define isel_stringIfyx(s) #s - #define isel_stringIfy(s) isel_stringIfyx(s) - - #define generateFunctionCall(t, function, ...) \ - _as->generateFunctionCallImp(t, isel_stringIfy(function), function, __VA_ARGS__) - - int prepareVariableArguments(V4IR::ExprList* args); - - typedef void (*ActivationMethod)(QV4::ExecutionContext *, QV4::Value *result, QV4::String *name, QV4::Value *args, int argc); - void callRuntimeMethodImp(V4IR::Temp *result, const char* name, ActivationMethod method, V4IR::Expr *base, V4IR::ExprList *args); -#define callRuntimeMethod(result, function, ...) \ - callRuntimeMethodImp(result, isel_stringIfy(function), function, __VA_ARGS__) - - uint addLookup(QV4::String *name); - uint addSetterLookup(QV4::String *name); - uint addGlobalLookup(QV4::String *name); - - template - void generateLookupCall(uint index, uint getterSetterOffset, Arg1 arg1, Arg2 arg2) - { - _as->loadPtr(Assembler::Address(Assembler::ContextRegister, offsetof(QV4::ExecutionContext, lookups)), - Assembler::ReturnValueRegister); - - Assembler::Pointer lookupAddr(Assembler::ReturnValueRegister, index * sizeof(QV4::Lookup)); - - Assembler::Address getterSetter = lookupAddr; - getterSetter.offset += getterSetterOffset; - - _as->generateFunctionCallImp(Assembler::Void, "lookup getter/setter", getterSetter, lookupAddr, arg1, arg2); - } - - template - void generateLookupCall(uint index, uint getterSetterOffset, Arg1 arg1) - { - generateLookupCall(index, getterSetterOffset, arg1, Assembler::VoidType()); - } - - V4IR::BasicBlock *_block; - V4IR::Function* _function; - QV4::Function* _vmFunction; - QVector _lookups; - Assembler* _as; - QSet _reentryBlocks; - int _locals; -}; - -class Q_QML_EXPORT ISelFactory: public EvalISelFactory -{ -public: - virtual ~ISelFactory() {} - virtual EvalInstructionSelection *create(QV4::ExecutionEngine *engine, V4IR::Module *module) - { return new InstructionSelection(engine, module); } - virtual bool jitCompileRegexps() const - { return true; } -}; - -} // end of namespace MASM -} // end of namespace QQmlJS - -QT_END_NAMESPACE - -#endif // QV4ISEL_MASM_P_H diff --git a/src/qml/qml/v4/qv4isel_p.cpp b/src/qml/qml/v4/qv4isel_p.cpp deleted file mode 100644 index ca8d249f9f..0000000000 --- a/src/qml/qml/v4/qv4isel_p.cpp +++ /dev/null @@ -1,440 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4debugging_p.h" -#include "qv4engine_p.h" -#include "qv4jsir_p.h" -#include "qv4isel_p.h" -#include "qv4isel_util_p.h" -#include "qv4functionobject_p.h" -#include "qv4function_p.h" - -#include - -#include - -namespace { -QTextStream qout(stderr, QIODevice::WriteOnly); -} // anonymous namespace - -using namespace QQmlJS; -using namespace QQmlJS::V4IR; - -EvalInstructionSelection::EvalInstructionSelection(QV4::ExecutionEngine *engine, Module *module) - : _engine(engine) - , useFastLookups(true) -{ - assert(engine); - assert(module); - - createFunctionMapping(0, module->rootFunction); - foreach (V4IR::Function *f, module->functions) { - assert(_irToVM.contains(f)); - } -} - -EvalInstructionSelection::~EvalInstructionSelection() -{} - -EvalISelFactory::~EvalISelFactory() -{} - -QV4::Function *EvalInstructionSelection::createFunctionMapping(QV4::Function *outer, Function *irFunction) -{ - QV4::Function *vmFunction = _engine->newFunction(irFunction->name ? *irFunction->name : QString()); - _irToVM.insert(irFunction, vmFunction); - - vmFunction->hasDirectEval = irFunction->hasDirectEval; - vmFunction->usesArgumentsObject = irFunction->usesArgumentsObject; - vmFunction->hasNestedFunctions = !irFunction->nestedFunctions.isEmpty(); - vmFunction->isStrict = irFunction->isStrict; - vmFunction->outer = outer; - vmFunction->isNamedExpression = irFunction->isNamedExpression; - vmFunction->sourceFile = irFunction->sourceFile; - - if (outer) - outer->nestedFunctions.append(vmFunction); - - foreach (const QString *formal, irFunction->formals) - if (formal) - vmFunction->formals.append(_engine->newString(*formal)); - foreach (const QString *local, irFunction->locals) - if (local) - vmFunction->locals.append(_engine->newString(*local)); - - foreach (V4IR::Function *function, irFunction->nestedFunctions) - createFunctionMapping(vmFunction, function); - - return vmFunction; -} - -QV4::Function *EvalInstructionSelection::vmFunction(Function *f) { - QV4::Function *function = _irToVM[f]; - if (!function->code) - run(function, f); - return function; -} - -void IRDecoder::visitMove(V4IR::Move *s) -{ - if (s->op == V4IR::OpInvalid) { - if (V4IR::Name *n = s->target->asName()) { - if (s->source->asTemp()) { - setActivationProperty(s->source->asTemp(), *n->id); - return; - } - } else if (V4IR::Temp *t = s->target->asTemp()) { - if (V4IR::Name *n = s->source->asName()) { - if (*n->id == QStringLiteral("this")) // TODO: `this' should be a builtin. - loadThisObject(t); - else - getActivationProperty(n, t); - return; - } else if (V4IR::Const *c = s->source->asConst()) { - loadConst(c, t); - return; - } else if (V4IR::Temp *t2 = s->source->asTemp()) { - copyValue(t2, t); - return; - } else if (V4IR::String *str = s->source->asString()) { - loadString(*str->value, t); - return; - } else if (V4IR::RegExp *re = s->source->asRegExp()) { - loadRegexp(re, t); - return; - } else if (V4IR::Closure *clos = s->source->asClosure()) { - initClosure(clos, t); - return; - } else if (V4IR::New *ctor = s->source->asNew()) { - if (Name *func = ctor->base->asName()) { - constructActivationProperty(func, ctor->args, t); - return; - } else if (V4IR::Member *member = ctor->base->asMember()) { - constructProperty(member->base->asTemp(), *member->name, ctor->args, t); - return; - } else if (V4IR::Temp *value = ctor->base->asTemp()) { - constructValue(value, ctor->args, t); - return; - } - } else if (V4IR::Member *m = s->source->asMember()) { - if (V4IR::Temp *base = m->base->asTemp()) { - getProperty(base, *m->name, t); - return; - } - } else if (V4IR::Subscript *ss = s->source->asSubscript()) { - getElement(ss->base->asTemp(), ss->index->asTemp(), t); - return; - } else if (V4IR::Unop *u = s->source->asUnop()) { - if (V4IR::Temp *e = u->expr->asTemp()) { - unop(u->op, e, t); - return; - } - } else if (V4IR::Binop *b = s->source->asBinop()) { - binop(b->op, b->left, b->right, t); - return; - } else if (V4IR::Call *c = s->source->asCall()) { - if (c->base->asName()) { - callBuiltin(c, t); - return; - } else if (Member *member = c->base->asMember()) { - Q_ASSERT(member->base->asTemp()); - callProperty(member->base->asTemp(), *member->name, c->args, t); - return; - } else if (Subscript *s = c->base->asSubscript()) { - Q_ASSERT(s->base->asTemp()); - Q_ASSERT(s->index->asTemp()); - callSubscript(s->base->asTemp(), s->index->asTemp(), c->args, t); - return; - } else if (V4IR::Temp *value = c->base->asTemp()) { - callValue(value, c->args, t); - return; - } - } else if (V4IR::Convert *c = s->source->asConvert()) { - Q_ASSERT(c->expr->asTemp()); - convertType(c->expr->asTemp(), t); - return; - } - } else if (V4IR::Member *m = s->target->asMember()) { - if (V4IR::Temp *base = m->base->asTemp()) { - if (s->source->asTemp()) { - setProperty(s->source->asTemp(), base, *m->name); - return; - } - } - } else if (V4IR::Subscript *ss = s->target->asSubscript()) { - if (s->source->asTemp()) { - setElement(s->source->asTemp(), ss->base->asTemp(), ss->index->asTemp()); - return; - } - } - } else { - // inplace assignment, e.g. x += 1, ++x, ... - if (V4IR::Temp *t = s->target->asTemp()) { - if (s->source->asTemp()) { - binop(s->op, t, s->source->asTemp(), t); - return; - } - } else if (V4IR::Name *n = s->target->asName()) { - if (s->source->asTemp()) { - inplaceNameOp(s->op, s->source->asTemp(), *n->id); - return; - } - } else if (V4IR::Subscript *ss = s->target->asSubscript()) { - if (s->source->asTemp()) { - inplaceElementOp(s->op, s->source->asTemp(), ss->base->asTemp(), - ss->index->asTemp()); - return; - } - } else if (V4IR::Member *m = s->target->asMember()) { - if (s->source->asTemp()) { - inplaceMemberOp(s->op, s->source->asTemp(), m->base->asTemp(), *m->name); - return; - } - } - } - - // For anything else...: - Q_UNIMPLEMENTED(); - s->dump(qout, V4IR::Stmt::MIR); - qout << endl; - assert(!"TODO"); -} - -IRDecoder::~IRDecoder() -{ -} - -void IRDecoder::visitExp(V4IR::Exp *s) -{ - if (V4IR::Call *c = s->expr->asCall()) { - // These are calls where the result is ignored. - if (c->base->asName()) { - callBuiltin(c, 0); - } else if (Temp *value = c->base->asTemp()) { - callValue(value, c->args, 0); - } else if (Member *member = c->base->asMember()) { - Q_ASSERT(member->base->asTemp()); - callProperty(member->base->asTemp(), *member->name, c->args, 0); - } else if (Subscript *s = c->base->asSubscript()) { - Q_ASSERT(s->base->asTemp()); - Q_ASSERT(s->index->asTemp()); - callSubscript(s->base->asTemp(), s->index->asTemp(), c->args, 0); - } else { - Q_UNIMPLEMENTED(); - } - } else { - Q_UNIMPLEMENTED(); - } -} - -void IRDecoder::callBuiltin(V4IR::Call *call, V4IR::Temp *result) -{ - V4IR::Name *baseName = call->base->asName(); - assert(baseName != 0); - - switch (baseName->builtin) { - case V4IR::Name::builtin_invalid: - callBuiltinInvalid(baseName, call->args, result); - return; - - case V4IR::Name::builtin_typeof: { - if (V4IR::Member *m = call->args->expr->asMember()) { - callBuiltinTypeofMember(m->base->asTemp(), *m->name, result); - return; - } else if (V4IR::Subscript *ss = call->args->expr->asSubscript()) { - callBuiltinTypeofSubscript(ss->base->asTemp(), ss->index->asTemp(), result); - return; - } else if (V4IR::Name *n = call->args->expr->asName()) { - callBuiltinTypeofName(*n->id, result); - return; - } else if (V4IR::Temp *arg = call->args->expr->asTemp()){ - assert(arg != 0); - callBuiltinTypeofValue(arg, result); - return; - } - } break; - - case V4IR::Name::builtin_delete: { - if (V4IR::Member *m = call->args->expr->asMember()) { - callBuiltinDeleteMember(m->base->asTemp(), *m->name, result); - return; - } else if (V4IR::Subscript *ss = call->args->expr->asSubscript()) { - callBuiltinDeleteSubscript(ss->base->asTemp(), ss->index->asTemp(), result); - return; - } else if (V4IR::Name *n = call->args->expr->asName()) { - callBuiltinDeleteName(*n->id, result); - return; - } else if (call->args->expr->asTemp()){ - // TODO: should throw in strict mode - callBuiltinDeleteValue(result); - return; - } - } break; - - case V4IR::Name::builtin_postincrement: { - if (V4IR::Member *m = call->args->expr->asMember()) { - callBuiltinPostIncrementMember(m->base->asTemp(), *m->name, result); - return; - } else if (V4IR::Subscript *ss = call->args->expr->asSubscript()) { - callBuiltinPostIncrementSubscript(ss->base->asTemp(), ss->index->asTemp(), result); - return; - } else if (V4IR::Name *n = call->args->expr->asName()) { - callBuiltinPostIncrementName(*n->id, result); - return; - } else if (V4IR::Temp *arg = call->args->expr->asTemp()){ - assert(arg != 0); - callBuiltinPostIncrementValue(arg, result); - return; - } - } break; - - case V4IR::Name::builtin_postdecrement: { - if (V4IR::Member *m = call->args->expr->asMember()) { - callBuiltinPostDecrementMember(m->base->asTemp(), *m->name, result); - return; - } else if (V4IR::Subscript *ss = call->args->expr->asSubscript()) { - callBuiltinPostDecrementSubscript(ss->base->asTemp(), ss->index->asTemp(), result); - return; - } else if (V4IR::Name *n = call->args->expr->asName()) { - callBuiltinPostDecrementName(*n->id, result); - return; - } else if (V4IR::Temp *arg = call->args->expr->asTemp()){ - assert(arg != 0); - callBuiltinPostDecrementValue(arg, result); - return; - } - } break; - - case V4IR::Name::builtin_throw: { - V4IR::Temp *arg = call->args->expr->asTemp(); - assert(arg != 0); - callBuiltinThrow(arg); - } return; - - case V4IR::Name::builtin_finish_try: - callBuiltinFinishTry(); - return; - - case V4IR::Name::builtin_foreach_iterator_object: { - V4IR::Temp *arg = call->args->expr->asTemp(); - assert(arg != 0); - callBuiltinForeachIteratorObject(arg, result); - } return; - - case V4IR::Name::builtin_foreach_next_property_name: { - V4IR::Temp *arg = call->args->expr->asTemp(); - assert(arg != 0); - callBuiltinForeachNextPropertyname(arg, result); - } return; - case V4IR::Name::builtin_push_with_scope: { - V4IR::Temp *arg = call->args->expr->asTemp(); - assert(arg != 0); - callBuiltinPushWithScope(arg); - } return; - - case V4IR::Name::builtin_pop_scope: - callBuiltinPopScope(); - return; - - case V4IR::Name::builtin_declare_vars: { - if (!call->args) - return; - V4IR::Const *deletable = call->args->expr->asConst(); - assert(deletable->type == V4IR::BoolType); - for (V4IR::ExprList *it = call->args->next; it; it = it->next) { - V4IR::Name *arg = it->expr->asName(); - assert(arg != 0); - callBuiltinDeclareVar(deletable->value != 0, *arg->id); - } - } return; - - case V4IR::Name::builtin_define_getter_setter: { - if (!call->args) - return; - V4IR::ExprList *args = call->args; - V4IR::Temp *object = args->expr->asTemp(); - assert(object); - args = args->next; - assert(args); - V4IR::Name *name = args->expr->asName(); - args = args->next; - assert(args); - V4IR::Temp *getter = args->expr->asTemp(); - args = args->next; - assert(args); - V4IR::Temp *setter = args->expr->asTemp(); - - callBuiltinDefineGetterSetter(object, *name->id, getter, setter); - } return; - - case V4IR::Name::builtin_define_property: { - if (!call->args) - return; - V4IR::ExprList *args = call->args; - V4IR::Temp *object = args->expr->asTemp(); - assert(object); - args = args->next; - assert(args); - V4IR::Name *name = args->expr->asName(); - args = args->next; - assert(args); - V4IR::Temp *value = args->expr->asTemp(); - - callBuiltinDefineProperty(object, *name->id, value); - } return; - - case V4IR::Name::builtin_define_array: - callBuiltinDefineArray(result, call->args); - return; - - case V4IR::Name::builtin_define_object_literal: - callBuiltinDefineObjectLiteral(result, call->args); - return; - - default: - break; - } - - Q_UNIMPLEMENTED(); - call->dump(qout); qout << endl; - assert(!"TODO!"); - Q_UNREACHABLE(); -} diff --git a/src/qml/qml/v4/qv4isel_p.h b/src/qml/qml/v4/qv4isel_p.h deleted file mode 100644 index ca49b4c8b1..0000000000 --- a/src/qml/qml/v4/qv4isel_p.h +++ /dev/null @@ -1,165 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QV4ISEL_P_H -#define QV4ISEL_P_H - -#include "qv4global_p.h" -#include "qv4jsir_p.h" - -#include -#include - -QT_BEGIN_NAMESPACE - -namespace QV4 { -struct ExecutionEngine; -struct Function; -} - -namespace QQmlJS { - -class Q_QML_EXPORT EvalInstructionSelection -{ -public: - EvalInstructionSelection(QV4::ExecutionEngine *engine, V4IR::Module *module); - virtual ~EvalInstructionSelection() = 0; - - QV4::Function *vmFunction(V4IR::Function *f); - - void setUseFastLookups(bool b) { useFastLookups = b; } - -protected: - QV4::Function *createFunctionMapping(QV4::Function *outer, V4IR::Function *irFunction); - QV4::ExecutionEngine *engine() const { return _engine; } - virtual void run(QV4::Function *vmFunction, V4IR::Function *function) = 0; - -private: - QV4::ExecutionEngine *_engine; - QHash _irToVM; -protected: - bool useFastLookups; -}; - -class Q_QML_EXPORT EvalISelFactory -{ -public: - virtual ~EvalISelFactory() = 0; - virtual EvalInstructionSelection *create(QV4::ExecutionEngine *engine, V4IR::Module *module) = 0; - virtual bool jitCompileRegexps() const = 0; -}; - -namespace V4IR { -class Q_QML_EXPORT IRDecoder: protected V4IR::StmtVisitor -{ -public: - virtual ~IRDecoder() = 0; - - virtual void visitPhi(V4IR::Phi *) {} - -public: // visitor methods for StmtVisitor: - virtual void visitMove(V4IR::Move *s); - virtual void visitExp(V4IR::Exp *s); - -public: // to implement by subclasses: - virtual void callBuiltinInvalid(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result) = 0; - virtual void callBuiltinTypeofMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) = 0; - virtual void callBuiltinTypeofSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) = 0; - virtual void callBuiltinTypeofName(const QString &name, V4IR::Temp *result) = 0; - virtual void callBuiltinTypeofValue(V4IR::Temp *value, V4IR::Temp *result) = 0; - virtual void callBuiltinDeleteMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) = 0; - virtual void callBuiltinDeleteSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) = 0; - virtual void callBuiltinDeleteName(const QString &name, V4IR::Temp *result) = 0; - virtual void callBuiltinDeleteValue(V4IR::Temp *result) = 0; - virtual void callBuiltinPostDecrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) = 0; - virtual void callBuiltinPostDecrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) = 0; - virtual void callBuiltinPostDecrementName(const QString &name, V4IR::Temp *result) = 0; - virtual void callBuiltinPostDecrementValue(V4IR::Temp *value, V4IR::Temp *result) = 0; - virtual void callBuiltinPostIncrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) = 0; - virtual void callBuiltinPostIncrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) = 0; - virtual void callBuiltinPostIncrementName(const QString &name, V4IR::Temp *result) = 0; - virtual void callBuiltinPostIncrementValue(V4IR::Temp *value, V4IR::Temp *result) = 0; - virtual void callBuiltinThrow(V4IR::Temp *arg) = 0; - virtual void callBuiltinFinishTry() = 0; - virtual void callBuiltinForeachIteratorObject(V4IR::Temp *arg, V4IR::Temp *result) = 0; - virtual void callBuiltinForeachNextPropertyname(V4IR::Temp *arg, V4IR::Temp *result) = 0; - virtual void callBuiltinPushWithScope(V4IR::Temp *arg) = 0; - virtual void callBuiltinPopScope() = 0; - virtual void callBuiltinDeclareVar(bool deletable, const QString &name) = 0; - virtual void callBuiltinDefineGetterSetter(V4IR::Temp *object, const QString &name, V4IR::Temp *getter, V4IR::Temp *setter) = 0; - virtual void callBuiltinDefineProperty(V4IR::Temp *object, const QString &name, V4IR::Temp *value) = 0; - virtual void callBuiltinDefineArray(V4IR::Temp *result, V4IR::ExprList *args) = 0; - virtual void callBuiltinDefineObjectLiteral(V4IR::Temp *result, V4IR::ExprList *args) = 0; - virtual void callValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result) = 0; - virtual void callProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result) = 0; - virtual void callSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::ExprList *args, V4IR::Temp *result) = 0; - virtual void convertType(V4IR::Temp *source, V4IR::Temp *target) = 0; - virtual void constructActivationProperty(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result) = 0; - virtual void constructProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result) = 0; - virtual void constructValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result) = 0; - virtual void loadThisObject(V4IR::Temp *temp) = 0; - virtual void loadConst(V4IR::Const *sourceConst, V4IR::Temp *targetTemp) = 0; - virtual void loadString(const QString &str, V4IR::Temp *targetTemp) = 0; - virtual void loadRegexp(V4IR::RegExp *sourceRegexp, V4IR::Temp *targetTemp) = 0; - virtual void getActivationProperty(const V4IR::Name *name, V4IR::Temp *temp) = 0; - virtual void setActivationProperty(V4IR::Temp *source, const QString &targetName) = 0; - virtual void initClosure(V4IR::Closure *closure, V4IR::Temp *target) = 0; - virtual void getProperty(V4IR::Temp *base, const QString &name, V4IR::Temp *target) = 0; - virtual void setProperty(V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName) = 0; - virtual void getElement(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *target) = 0; - virtual void setElement(V4IR::Temp *source, V4IR::Temp *targetBase, V4IR::Temp *targetIndex) = 0; - virtual void copyValue(V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp) = 0; - virtual void unop(V4IR::AluOp oper, V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp) = 0; - virtual void binop(V4IR::AluOp oper, V4IR::Expr *leftSource, V4IR::Expr *rightSource, V4IR::Temp *target) = 0; - virtual void inplaceNameOp(V4IR::AluOp oper, V4IR::Temp *rightSource, const QString &targetName) = 0; - virtual void inplaceElementOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBaseTemp, V4IR::Temp *targetIndexTemp) = 0; - virtual void inplaceMemberOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName) = 0; - -private: - void callBuiltin(V4IR::Call *c, V4IR::Temp *temp); -}; -} // namespace IR - -} // namespace QQmlJS - -QT_END_NAMESPACE - -#endif // QV4ISEL_P_H diff --git a/src/qml/qml/v4/qv4isel_util_p.h b/src/qml/qml/v4/qv4isel_util_p.h deleted file mode 100644 index 5aedaaff1b..0000000000 --- a/src/qml/qml/v4/qv4isel_util_p.h +++ /dev/null @@ -1,87 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QV4ISEL_UTIL_P_H -#define QV4ISEL_UTIL_P_H - -#include "qv4runtime_p.h" -#include "qv4jsir_p.h" - -QT_BEGIN_NAMESPACE - -namespace QQmlJS { - -inline QV4::Value convertToValue(V4IR::Const *c) -{ - switch (c->type) { - case V4IR::MissingType: - return QV4::Value::emptyValue(); - case V4IR::NullType: - return QV4::Value::nullValue(); - case V4IR::UndefinedType: - return QV4::Value::undefinedValue(); - case V4IR::BoolType: - return QV4::Value::fromBoolean(c->value != 0); - case V4IR::SInt32Type: - return QV4::Value::fromInt32(int(c->value)); - case V4IR::UInt32Type: - return QV4::Value::fromUInt32(unsigned(c->value)); - case V4IR::DoubleType: - return QV4::Value::fromDouble(c->value); - case V4IR::NumberType: { - int ival = (int)c->value; - // +0 != -0, so we need to convert to double when negating 0 - if (ival == c->value && !(c->value == 0 && isNegative(c->value))) { - return QV4::Value::fromInt32(ival); - } else { - return QV4::Value::fromDouble(c->value); - } - } - default: - Q_UNREACHABLE(); - } -} - -} // namespace QQmlJS - -QT_END_NAMESPACE - -#endif // QV4ISEL_UTIL_P_H diff --git a/src/qml/qml/v4/qv4jsir.cpp b/src/qml/qml/v4/qv4jsir.cpp deleted file mode 100644 index 7f8d257429..0000000000 --- a/src/qml/qml/v4/qv4jsir.cpp +++ /dev/null @@ -1,1024 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4jsir_p.h" -#include - -#include -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE - -namespace QQmlJS { -namespace V4IR { - -QString typeName(Type t) -{ - switch (t) { - case UnknownType: return QStringLiteral(""); - case MissingType: return QStringLiteral("missing"); - case UndefinedType: return QStringLiteral("undefined"); - case NullType: return QStringLiteral("null"); - case BoolType: return QStringLiteral("bool"); - case UInt32Type: return QStringLiteral("uint32"); - case SInt32Type: return QStringLiteral("int32"); - case DoubleType: return QStringLiteral("double"); - case NumberType: return QStringLiteral("number"); - case StringType: return QStringLiteral("string"); - case ObjectType: return QStringLiteral("object"); - default: return QStringLiteral("multiple"); - } -} - -const char *opname(AluOp op) -{ - switch (op) { - case OpInvalid: return "?"; - - case OpIfTrue: return "(bool)"; - case OpNot: return "!"; - case OpUMinus: return "-"; - case OpUPlus: return "+"; - case OpCompl: return "~"; - case OpIncrement: return "++"; - case OpDecrement: return "--"; - - case OpBitAnd: return "&"; - case OpBitOr: return "|"; - case OpBitXor: return "^"; - - case OpAdd: return "+"; - case OpSub: return "-"; - case OpMul: return "*"; - case OpDiv: return "/"; - case OpMod: return "%"; - - case OpLShift: return "<<"; - case OpRShift: return ">>"; - case OpURShift: return ">>>"; - - case OpGt: return ">"; - case OpLt: return "<"; - case OpGe: return ">="; - case OpLe: return "<="; - case OpEqual: return "=="; - case OpNotEqual: return "!="; - case OpStrictEqual: return "==="; - case OpStrictNotEqual: return "!=="; - - case OpInstanceof: return "instanceof"; - case OpIn: return "in"; - - case OpAnd: return "&&"; - case OpOr: return "||"; - - default: return "?"; - - } // switch -} - -AluOp binaryOperator(int op) -{ - switch (static_cast(op)) { - case QSOperator::Add: return OpAdd; - case QSOperator::And: return OpAnd; - case QSOperator::BitAnd: return OpBitAnd; - case QSOperator::BitOr: return OpBitOr; - case QSOperator::BitXor: return OpBitXor; - case QSOperator::Div: return OpDiv; - case QSOperator::Equal: return OpEqual; - case QSOperator::Ge: return OpGe; - case QSOperator::Gt: return OpGt; - case QSOperator::Le: return OpLe; - case QSOperator::LShift: return OpLShift; - case QSOperator::Lt: return OpLt; - case QSOperator::Mod: return OpMod; - case QSOperator::Mul: return OpMul; - case QSOperator::NotEqual: return OpNotEqual; - case QSOperator::Or: return OpOr; - case QSOperator::RShift: return OpRShift; - case QSOperator::StrictEqual: return OpStrictEqual; - case QSOperator::StrictNotEqual: return OpStrictNotEqual; - case QSOperator::Sub: return OpSub; - case QSOperator::URShift: return OpURShift; - case QSOperator::InstanceOf: return OpInstanceof; - case QSOperator::In: return OpIn; - default: return OpInvalid; - } -} - -struct RemoveSharedExpressions: V4IR::StmtVisitor, V4IR::ExprVisitor -{ - CloneExpr clone; - QSet subexpressions; // contains all the non-cloned subexpressions in the given function - Expr *uniqueExpr; - - RemoveSharedExpressions(): uniqueExpr(0) {} - - void operator()(V4IR::Function *function) - { - subexpressions.clear(); - - foreach (BasicBlock *block, function->basicBlocks) { - clone.setBasicBlock(block); - - foreach (Stmt *s, block->statements) { - s->accept(this); - } - } - } - - template - _Expr *cleanup(_Expr *expr) - { - if (subexpressions.contains(expr)) { - // the cloned expression is unique by definition - // so we don't need to add it to `subexpressions'. - return clone(expr); - } - - subexpressions.insert(expr); - V4IR::Expr *e = expr; - qSwap(uniqueExpr, e); - expr->accept(this); - qSwap(uniqueExpr, e); - return static_cast<_Expr *>(e); - } - - // statements - virtual void visitExp(Exp *s) - { - s->expr = cleanup(s->expr); - } - - virtual void visitMove(Move *s) - { - s->target = cleanup(s->target); - s->source = cleanup(s->source); - } - - virtual void visitJump(Jump *) - { - // nothing to do for Jump statements - } - - virtual void visitCJump(CJump *s) - { - s->cond = cleanup(s->cond); - } - - virtual void visitRet(Ret *s) - { - s->expr = cleanup(s->expr); - } - - virtual void visitTry(Try *) - { - // nothing to do for Try statements - } - - virtual void visitPhi(V4IR::Phi *) { Q_UNIMPLEMENTED(); abort(); } - - // expressions - virtual void visitConst(Const *) {} - virtual void visitString(String *) {} - virtual void visitRegExp(RegExp *) {} - virtual void visitName(Name *) {} - virtual void visitTemp(Temp *) {} - virtual void visitClosure(Closure *) {} - - virtual void visitConvert(Convert *e) - { - e->expr = cleanup(e->expr); - } - - virtual void visitUnop(Unop *e) - { - e->expr = cleanup(e->expr); - } - - virtual void visitBinop(Binop *e) - { - e->left = cleanup(e->left); - e->right = cleanup(e->right); - } - - virtual void visitCall(Call *e) - { - e->base = cleanup(e->base); - for (V4IR::ExprList *it = e->args; it; it = it->next) - it->expr = cleanup(it->expr); - } - - virtual void visitNew(New *e) - { - e->base = cleanup(e->base); - for (V4IR::ExprList *it = e->args; it; it = it->next) - it->expr = cleanup(it->expr); - } - - virtual void visitSubscript(Subscript *e) - { - e->base = cleanup(e->base); - e->index = cleanup(e->index); - } - - virtual void visitMember(Member *e) - { - e->base = cleanup(e->base); - } -}; - -static QString dumpStart(const Expr *e) { - if (e->type == UnknownType) -// return QStringLiteral("**UNKNOWN**"); - return QString(); - else - return typeName(e->type) + QStringLiteral("{"); -} - -static const char *dumpEnd(const Expr *e) { - if (e->type == UnknownType) - return ""; - else - return "}"; -} - -void Const::dump(QTextStream &out) const -{ - if (type != UndefinedType && type != NullType) - out << dumpStart(this); - switch (type) { - case QQmlJS::V4IR::UndefinedType: - out << "undefined"; - break; - case QQmlJS::V4IR::NullType: - out << "null"; - break; - case QQmlJS::V4IR::BoolType: - out << (value ? "true" : "false"); - break; - case QQmlJS::V4IR::MissingType: - out << "missing"; - break; - default: - out << QString::number(value, 'g', 16); - break; - } - if (type != UndefinedType && type != NullType) - out << dumpEnd(this); -} - -void String::dump(QTextStream &out) const -{ - out << '"' << escape(*value) << '"'; -} - -QString String::escape(const QString &s) -{ - QString r; - for (int i = 0; i < s.length(); ++i) { - const QChar ch = s.at(i); - if (ch == QLatin1Char('\n')) - r += QStringLiteral("\\n"); - else if (ch == QLatin1Char('\r')) - r += QStringLiteral("\\r"); - else if (ch == QLatin1Char('\\')) - r += QStringLiteral("\\\\"); - else if (ch == QLatin1Char('"')) - r += QStringLiteral("\\\""); - else if (ch == QLatin1Char('\'')) - r += QStringLiteral("\\'"); - else - r += ch; - } - return r; -} - -void RegExp::dump(QTextStream &out) const -{ - char f[3]; - int i = 0; - if (flags & RegExp_Global) - f[i++] = 'g'; - if (flags & RegExp_IgnoreCase) - f[i++] = 'i'; - if (flags & RegExp_Multiline) - f[i++] = 'm'; - f[i] = 0; - - out << '/' << *value << '/' << f; -} - -void Name::initGlobal(const QString *id, quint32 line, quint32 column) -{ - this->id = id; - this->builtin = builtin_invalid; - this->global = true; - this->line = line; - this->column = column; -} - -void Name::init(const QString *id, quint32 line, quint32 column) -{ - this->id = id; - this->builtin = builtin_invalid; - this->global = false; - this->line = line; - this->column = column; -} - -void Name::init(Builtin builtin, quint32 line, quint32 column) -{ - this->id = 0; - this->builtin = builtin; - this->global = false; - this->line = line; - this->column = column; -} - -static const char *builtin_to_string(Name::Builtin b) -{ - switch (b) { - case Name::builtin_invalid: - return "builtin_invalid"; - case Name::builtin_typeof: - return "builtin_typeof"; - case Name::builtin_delete: - return "builtin_delete"; - case Name::builtin_postincrement: - return "builtin_postincrement"; - case Name::builtin_postdecrement: - return "builtin_postdecrement"; - case Name::builtin_throw: - return "builtin_throw"; - case Name::builtin_finish_try: - return "builtin_finish_try"; - case V4IR::Name::builtin_foreach_iterator_object: - return "builtin_foreach_iterator_object"; - case V4IR::Name::builtin_foreach_next_property_name: - return "builtin_foreach_next_property_name"; - case V4IR::Name::builtin_push_with_scope: - return "builtin_push_with_scope"; - case V4IR::Name::builtin_pop_scope: - return "builtin_pop_scope"; - case V4IR::Name::builtin_declare_vars: - return "builtin_declare_vars"; - case V4IR::Name::builtin_define_property: - return "builtin_define_property"; - case V4IR::Name::builtin_define_array: - return "builtin_define_array"; - case V4IR::Name::builtin_define_getter_setter: - return "builtin_define_getter_setter"; - case V4IR::Name::builtin_define_object_literal: - return "builtin_define_object_literal"; - } - return "builtin_(###FIXME)"; -}; - -void Name::dump(QTextStream &out) const -{ - if (id) - out << *id; - else - out << builtin_to_string(builtin); -} - -void Temp::dump(QTextStream &out) const -{ - out << dumpStart(this); - switch (kind) { - case Formal: out << '#' << index; break; - case ScopedFormal: out << '#' << index - << '@' << scope; break; - case Local: out << '$' << index; break; - case ScopedLocal: out << '$' << index - << '@' << scope; break; - case VirtualRegister: out << '%' << index; break; - default: out << "INVALID"; - } - out << dumpEnd(this); -} - -bool operator<(const Temp &t1, const Temp &t2) Q_DECL_NOTHROW -{ - if (t1.kind < t2.kind) return true; - if (t1.kind > t2.kind) return false; - if (t1.index < t2.index) return true; - if (t1.index > t2.index) return false; - return t1.scope < t2.scope; -} - -void Closure::dump(QTextStream &out) const -{ - QString name = value->name ? *value->name : QString(); - if (name.isEmpty()) - name.sprintf("%p", value); - out << "closure(" << name << ')'; -} - -void Convert::dump(QTextStream &out) const -{ - out << dumpStart(this); - out << "convert("; - expr->dump(out); - out << ')' << dumpEnd(this); -} - -void Unop::dump(QTextStream &out) const -{ - out << dumpStart(this) << opname(op); - expr->dump(out); - out << dumpEnd(this); -} - -void Binop::dump(QTextStream &out) const -{ - out << dumpStart(this); - left->dump(out); - out << ' ' << opname(op) << ' '; - right->dump(out); - out << dumpEnd(this); -} - -void Call::dump(QTextStream &out) const -{ - base->dump(out); - out << '('; - for (ExprList *it = args; it; it = it->next) { - if (it != args) - out << ", "; - it->expr->dump(out); - } - out << ')'; -} - -void New::dump(QTextStream &out) const -{ - out << "new "; - base->dump(out); - out << '('; - for (ExprList *it = args; it; it = it->next) { - if (it != args) - out << ", "; - it->expr->dump(out); - } - out << ')'; -} - -void Subscript::dump(QTextStream &out) const -{ - base->dump(out); - out << '['; - index->dump(out); - out << ']'; -} - -void Member::dump(QTextStream &out) const -{ - base->dump(out); - out << '.' << *name; -} - -void Exp::dump(QTextStream &out, Mode) -{ - out << "(void) "; - expr->dump(out); - out << ';'; -} - -void Move::dump(QTextStream &out, Mode) -{ - target->dump(out); - out << ' '; - if (op != OpInvalid) - out << opname(op); - out << "= "; -// if (source->type != target->type) -// out << typeName(source->type) << "_to_" << typeName(target->type) << '('; - source->dump(out); -// if (source->type != target->type) -// out << ')'; - out << ';'; -} - -void Jump::dump(QTextStream &out, Mode mode) -{ - Q_UNUSED(mode); - out << "goto " << 'L' << target->index << ';'; -} - -void CJump::dump(QTextStream &out, Mode mode) -{ - Q_UNUSED(mode); - out << "if ("; - cond->dump(out); - if (mode == HIR) - out << ") goto " << 'L' << iftrue->index << "; else goto " << 'L' << iffalse->index << ';'; - else - out << ") goto " << 'L' << iftrue->index << ";"; -} - -void Ret::dump(QTextStream &out, Mode) -{ - out << "return"; - if (expr) { - out << ' '; - expr->dump(out); - } - out << ';'; -} - -void Try::dump(QTextStream &out, Stmt::Mode mode) -{ - out << "try L" << tryBlock->index << "; catch exception in "; - exceptionVar->dump(out); - out << " with the name " << exceptionVarName << " and go to L" << catchBlock->index << ';'; -} - -void Phi::dump(QTextStream &out, Stmt::Mode mode) -{ - targetTemp->dump(out); - out << " = phi("; - for (int i = 0, ei = incoming.size(); i < ei; ++i) { - if (i > 0) - out << ", "; - if (incoming[i]) - incoming[i]->dump(out); - } - out << ");"; -} - -Function *Module::newFunction(const QString &name, Function *outer) -{ - Function *f = new Function(this, outer, name); - functions.append(f); - if (!outer) { - assert(!rootFunction); - rootFunction = f; - } else { - outer->nestedFunctions.append(f); - } - return f; -} - -Module::~Module() -{ - foreach (Function *f, functions) { - delete f; - } -} - -Function::~Function() -{ - // destroy the Stmt::Data blocks manually, because memory pool cleanup won't - // call the Stmt destructors. - foreach (V4IR::BasicBlock *b, basicBlocks) - foreach (V4IR::Stmt *s, b->statements) - s->destroyData(); - - qDeleteAll(basicBlocks); - pool = 0; - module = 0; -} - - -const QString *Function::newString(const QString &text) -{ - return &*strings.insert(text); -} - -BasicBlock *Function::newBasicBlock(BasicBlock *containingLoop, BasicBlockInsertMode mode) -{ - BasicBlock *block = new BasicBlock(this, containingLoop); - return mode == InsertBlock ? insertBasicBlock(block) : block; -} - -void Function::dump(QTextStream &out, Stmt::Mode mode) -{ - QString n = name ? *name : QString(); - if (n.isEmpty()) - n.sprintf("%p", this); - out << "function " << n << "() {" << endl; - foreach (const QString *formal, formals) - out << "\treceive " << *formal << ';' << endl; - foreach (const QString *local, locals) - out << "\tlocal " << *local << ';' << endl; - foreach (BasicBlock *bb, basicBlocks) - bb->dump(out, mode); - out << '}' << endl; -} - -void Function::removeSharedExpressions() -{ - RemoveSharedExpressions removeSharedExpressions; - removeSharedExpressions(this); -} - -int Function::indexOfArgument(const QStringRef &string) const -{ - for (int i = formals.size() - 1; i >= 0; --i) { - if (*formals.at(i) == string) - return i; - } - return -1; -} -unsigned BasicBlock::newTemp() -{ - return function->tempCount++; -} - -Temp *BasicBlock::TEMP(unsigned index) -{ - Temp *e = function->New(); - e->init(Temp::VirtualRegister, index, 0); - return e; -} - -Temp *BasicBlock::ARG(unsigned index, unsigned scope) -{ - Temp *e = function->New(); - e->init(scope ? Temp::ScopedFormal : Temp::Formal, index, scope); - return e; -} - -Temp *BasicBlock::LOCAL(unsigned index, unsigned scope) -{ - Temp *e = function->New(); - e->init(scope ? Temp::ScopedLocal : Temp::Local, index, scope); - return e; -} - -Expr *BasicBlock::CONST(Type type, double value) -{ - Const *e = function->New(); - if (type == NumberType) { - int ival = (int)value; - // +0 != -0, so we need to convert to double when negating 0 - if (ival == value && !(value == 0 && isNegative(value))) - type = SInt32Type; - else - type = DoubleType; - } - e->init(type, value); - return e; -} - -Expr *BasicBlock::STRING(const QString *value) -{ - String *e = function->New(); - e->init(value); - return e; -} - -Expr *BasicBlock::REGEXP(const QString *value, int flags) -{ - RegExp *e = function->New(); - e->init(value, flags); - return e; -} - -Name *BasicBlock::NAME(const QString &id, quint32 line, quint32 column) -{ - Name *e = function->New(); - e->init(function->newString(id), line, column); - return e; -} - -Name *BasicBlock::GLOBALNAME(const QString &id, quint32 line, quint32 column) -{ - Name *e = function->New(); - e->initGlobal(function->newString(id), line, column); - return e; -} - - -Name *BasicBlock::NAME(Name::Builtin builtin, quint32 line, quint32 column) -{ - Name *e = function->New(); - e->init(builtin, line, column); - return e; -} - -Closure *BasicBlock::CLOSURE(Function *function) -{ - Closure *clos = function->New(); - clos->init(function); - return clos; -} - -Expr *BasicBlock::CONVERT(Expr *expr, Type type) -{ - Convert *e = function->New(); - e->init(expr, type); - return e; -} - -Expr *BasicBlock::UNOP(AluOp op, Expr *expr) -{ - Unop *e = function->New(); - e->init(op, expr); - return e; -} - -Expr *BasicBlock::BINOP(AluOp op, Expr *left, Expr *right) -{ - Binop *e = function->New(); - e->init(op, left, right); - return e; -} - -Expr *BasicBlock::CALL(Expr *base, ExprList *args) -{ - Call *e = function->New(); - e->init(base, args); - int argc = 0; - for (ExprList *it = args; it; it = it->next) - ++argc; - function->maxNumberOfArguments = qMax(function->maxNumberOfArguments, argc); - return e; -} - -Expr *BasicBlock::NEW(Expr *base, ExprList *args) -{ - New *e = function->New(); - e->init(base, args); - return e; -} - -Expr *BasicBlock::SUBSCRIPT(Expr *base, Expr *index) -{ - Subscript *e = function->New(); - e->init(base, index); - return e; -} - -Expr *BasicBlock::MEMBER(Expr *base, const QString *name) -{ - Member*e = function->New(); - e->init(base, name); - return e; -} - -Stmt *BasicBlock::EXP(Expr *expr) -{ - if (isTerminated()) - return 0; - - Exp *s = function->New(); - s->init(expr); - appendStatement(s); - return s; -} - -Stmt *BasicBlock::MOVE(Expr *target, Expr *source, AluOp op) -{ - if (isTerminated()) - return 0; - - Move *s = function->New(); - s->init(target, source, op); - appendStatement(s); - return s; -} - -Stmt *BasicBlock::JUMP(BasicBlock *target) -{ - if (isTerminated()) - return 0; - - Jump *s = function->New(); - s->init(target); - appendStatement(s); - - assert(! out.contains(target)); - out.append(target); - - assert(! target->in.contains(this)); - target->in.append(this); - - return s; -} - -Stmt *BasicBlock::CJUMP(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse) -{ - if (isTerminated()) - return 0; - - if (iftrue == iffalse) { - MOVE(TEMP(newTemp()), cond); - return JUMP(iftrue); - } - - CJump *s = function->New(); - s->init(cond, iftrue, iffalse); - appendStatement(s); - - assert(! out.contains(iftrue)); - out.append(iftrue); - - assert(! iftrue->in.contains(this)); - iftrue->in.append(this); - - assert(! out.contains(iffalse)); - out.append(iffalse); - - assert(! iffalse->in.contains(this)); - iffalse->in.append(this); - - return s; -} - -Stmt *BasicBlock::RET(Temp *expr) -{ - if (isTerminated()) - return 0; - - Ret *s = function->New(); - s->init(expr); - appendStatement(s); - return s; -} - -Stmt *BasicBlock::TRY(BasicBlock *tryBlock, BasicBlock *catchBlock, const QString &exceptionVarName, Temp *exceptionVar) -{ - if (isTerminated()) - return 0; - - Try *t = function->New(); - t->init(tryBlock, catchBlock, exceptionVarName, exceptionVar); - appendStatement(t); - - assert(! out.contains(tryBlock)); - out.append(tryBlock); - - assert(! out.contains(catchBlock)); - out.append(catchBlock); - - assert(! tryBlock->in.contains(this)); - tryBlock->in.append(this); - - assert(! catchBlock->in.contains(this)); - catchBlock->in.append(this); - - return t; -} - -void BasicBlock::dump(QTextStream &out, Stmt::Mode mode) -{ - out << 'L' << index << ':' << endl; - foreach (Stmt *s, statements) { - out << '\t'; - s->dump(out, mode); - - if (s->location.isValid()) - out << " // line: " << s->location.startLine << " ; column: " << s->location.startColumn; - - out << endl; - } -} - -void BasicBlock::appendStatement(Stmt *statement) -{ - if (nextLocation.isValid()) { - statement->location = nextLocation; - nextLocation = AST::SourceLocation(); - } - statements.append(statement); -} - -CloneExpr::CloneExpr(BasicBlock *block) - : block(block), cloned(0) -{ -} - -void CloneExpr::setBasicBlock(BasicBlock *block) -{ - this->block = block; -} - -ExprList *CloneExpr::clone(ExprList *list) -{ - if (! list) - return 0; - - ExprList *clonedList = block->function->New(); - clonedList->init(clone(list->expr), clone(list->next)); - return clonedList; -} - -void CloneExpr::visitConst(Const *e) -{ - cloned = block->CONST(e->type, e->value); -} - -void CloneExpr::visitString(String *e) -{ - cloned = block->STRING(e->value); -} - -void CloneExpr::visitRegExp(RegExp *e) -{ - cloned = block->REGEXP(e->value, e->flags); -} - -void CloneExpr::visitName(Name *e) -{ - if (e->id) - cloned = block->NAME(*e->id, e->line, e->column); - else - cloned = block->NAME(e->builtin, e->line, e->column); -} - -void CloneExpr::visitTemp(Temp *e) -{ - Temp *t = block->function->New(); - t->init(e->kind, e->index, e->scope); - cloned = t; -} - -void CloneExpr::visitClosure(Closure *e) -{ - cloned = block->CLOSURE(e->value); -} - -void CloneExpr::visitConvert(Convert *e) -{ - cloned = block->CONVERT(clone(e->expr), e->type); -} - -void CloneExpr::visitUnop(Unop *e) -{ - cloned = block->UNOP(e->op, clone(e->expr)); -} - -void CloneExpr::visitBinop(Binop *e) -{ - cloned = block->BINOP(e->op, clone(e->left), clone(e->right)); -} - -void CloneExpr::visitCall(Call *e) -{ - cloned = block->CALL(clone(e->base), clone(e->args)); -} - -void CloneExpr::visitNew(New *e) -{ - cloned = block->NEW(clone(e->base), clone(e->args)); -} - -void CloneExpr::visitSubscript(Subscript *e) -{ - cloned = block->SUBSCRIPT(clone(e->base), clone(e->index)); -} - -void CloneExpr::visitMember(Member *e) -{ - cloned = block->MEMBER(clone(e->base), e->name); -} - -} // end of namespace IR -} // end of namespace QQmlJS - -QT_END_NAMESPACE diff --git a/src/qml/qml/v4/qv4jsir_p.h b/src/qml/qml/v4/qv4jsir_p.h deleted file mode 100644 index 659d9870b1..0000000000 --- a/src/qml/qml/v4/qv4jsir_p.h +++ /dev/null @@ -1,890 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QV4JSIR_P_H -#define QV4JSIR_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "qv4global_p.h" -#include -#include - -#include -#include -#include -#include - -#ifdef CONST -#undef CONST -#endif - -QT_BEGIN_NAMESPACE - -class QTextStream; -class QQmlType; - -namespace QV4 { -struct ExecutionContext; -} - -namespace QQmlJS { - -inline bool isNegative(double d) -{ - uchar *dch = (uchar *)&d; - if (QSysInfo::ByteOrder == QSysInfo::BigEndian) - return (dch[0] & 0x80); - else - return (dch[7] & 0x80); - -} - -namespace V4IR { - -struct BasicBlock; -struct Function; -struct Module; - -struct Stmt; -struct Expr; - -// expressions -struct Const; -struct String; -struct RegExp; -struct Name; -struct Temp; -struct Closure; -struct Convert; -struct Unop; -struct Binop; -struct Call; -struct New; -struct Subscript; -struct Member; - -// statements -struct Exp; -struct Move; -struct Jump; -struct CJump; -struct Ret; -struct Try; -struct Phi; - -enum AluOp { - OpInvalid = 0, - - OpIfTrue, - OpNot, - OpUMinus, - OpUPlus, - OpCompl, - OpIncrement, - OpDecrement, - - OpBitAnd, - OpBitOr, - OpBitXor, - - OpAdd, - OpSub, - OpMul, - OpDiv, - OpMod, - - OpLShift, - OpRShift, - OpURShift, - - OpGt, - OpLt, - OpGe, - OpLe, - OpEqual, - OpNotEqual, - OpStrictEqual, - OpStrictNotEqual, - - OpInstanceof, - OpIn, - - OpAnd, - OpOr, - - LastAluOp = OpOr -}; -AluOp binaryOperator(int op); -const char *opname(V4IR::AluOp op); - -enum Type { - UnknownType = 0, - - MissingType = 1 << 0, - UndefinedType = 1 << 1, - NullType = 1 << 2, - BoolType = 1 << 3, - - SInt32Type = 1 << 4, - UInt32Type = 1 << 5, - DoubleType = 1 << 6, - NumberType = SInt32Type | UInt32Type | DoubleType, - - StringType = 1 << 7, - ObjectType = 1 << 8 -}; -QString typeName(Type t); - -struct ExprVisitor { - virtual ~ExprVisitor() {} - virtual void visitConst(Const *) = 0; - virtual void visitString(String *) = 0; - virtual void visitRegExp(RegExp *) = 0; - virtual void visitName(Name *) = 0; - virtual void visitTemp(Temp *) = 0; - virtual void visitClosure(Closure *) = 0; - virtual void visitConvert(Convert *) = 0; - virtual void visitUnop(Unop *) = 0; - virtual void visitBinop(Binop *) = 0; - virtual void visitCall(Call *) = 0; - virtual void visitNew(New *) = 0; - virtual void visitSubscript(Subscript *) = 0; - virtual void visitMember(Member *) = 0; -}; - -struct StmtVisitor { - virtual ~StmtVisitor() {} - virtual void visitExp(Exp *) = 0; - virtual void visitMove(Move *) = 0; - virtual void visitJump(Jump *) = 0; - virtual void visitCJump(CJump *) = 0; - virtual void visitRet(Ret *) = 0; - virtual void visitTry(Try *) = 0; - virtual void visitPhi(Phi *) = 0; -}; - -struct Expr { - Type type; - - Expr(): type(UnknownType) {} - virtual ~Expr() {} - virtual void accept(ExprVisitor *) = 0; - virtual bool isLValue() { return false; } - virtual Const *asConst() { return 0; } - virtual String *asString() { return 0; } - virtual RegExp *asRegExp() { return 0; } - virtual Name *asName() { return 0; } - virtual Temp *asTemp() { return 0; } - virtual Closure *asClosure() { return 0; } - virtual Convert *asConvert() { return 0; } - virtual Unop *asUnop() { return 0; } - virtual Binop *asBinop() { return 0; } - virtual Call *asCall() { return 0; } - virtual New *asNew() { return 0; } - virtual Subscript *asSubscript() { return 0; } - virtual Member *asMember() { return 0; } - virtual void dump(QTextStream &out) const = 0; -}; - -struct ExprList { - Expr *expr; - ExprList *next; - - void init(Expr *expr, ExprList *next = 0) - { - this->expr = expr; - this->next = next; - } -}; - -struct Const: Expr { - double value; - - void init(Type type, double value) - { - this->type = type; - this->value = value; - } - - virtual void accept(ExprVisitor *v) { v->visitConst(this); } - virtual Const *asConst() { return this; } - - virtual void dump(QTextStream &out) const; -}; - -struct String: Expr { - const QString *value; - - void init(const QString *value) - { - this->value = value; - } - - virtual void accept(ExprVisitor *v) { v->visitString(this); } - virtual String *asString() { return this; } - - virtual void dump(QTextStream &out) const; - static QString escape(const QString &s); -}; - -struct RegExp: Expr { - // needs to be compatible with the flags in the lexer, and in RegExpObject - enum Flags { - RegExp_Global = 0x01, - RegExp_IgnoreCase = 0x02, - RegExp_Multiline = 0x04 - }; - - const QString *value; - int flags; - - void init(const QString *value, int flags) - { - this->value = value; - this->flags = flags; - } - - virtual void accept(ExprVisitor *v) { v->visitRegExp(this); } - virtual RegExp *asRegExp() { return this; } - - virtual void dump(QTextStream &out) const; -}; - -struct Name: Expr { - enum Builtin { - builtin_invalid, - builtin_typeof, - builtin_delete, - builtin_postincrement, - builtin_postdecrement, - builtin_throw, - builtin_finish_try, - builtin_foreach_iterator_object, - builtin_foreach_next_property_name, - builtin_push_with_scope, - builtin_pop_scope, - builtin_declare_vars, - builtin_define_property, - builtin_define_array, - builtin_define_getter_setter, - builtin_define_object_literal - }; - - const QString *id; - Builtin builtin; - bool global; - quint32 line; - quint32 column; - - void initGlobal(const QString *id, quint32 line, quint32 column); - void init(const QString *id, quint32 line, quint32 column); - void init(Builtin builtin, quint32 line, quint32 column); - - virtual void accept(ExprVisitor *v) { v->visitName(this); } - virtual bool isLValue() { return true; } - virtual Name *asName() { return this; } - - virtual void dump(QTextStream &out) const; -}; - -struct Temp: Expr { - enum Kind { - Formal = 0, - ScopedFormal, - Local, - ScopedLocal, - VirtualRegister, - PhysicalRegister, - StackSlot - }; - - unsigned index; - unsigned scope : 29; // how many scopes outside the current one? - unsigned kind : 3; - - void init(unsigned kind, unsigned index, unsigned scope) - { - Q_ASSERT((kind == ScopedLocal && scope != 0) || - (kind == ScopedFormal && scope != 0) || - (scope == 0)); - - this->kind = kind; - this->index = index; - this->scope = scope; - } - - virtual void accept(ExprVisitor *v) { v->visitTemp(this); } - virtual bool isLValue() { return true; } - virtual Temp *asTemp() { return this; } - - virtual void dump(QTextStream &out) const; -}; - -inline bool operator==(const Temp &t1, const Temp &t2) Q_DECL_NOTHROW -{ return t1.index == t2.index && t1.scope == t2.scope && t1.kind == t2.kind; } - -inline uint qHash(const Temp &t, uint seed = 0) Q_DECL_NOTHROW -{ return t.index ^ (t.kind | (t.scope << 3)) ^ seed; } - -bool operator<(const Temp &t1, const Temp &t2) Q_DECL_NOTHROW; - -struct Closure: Expr { - Function *value; - - void init(Function *value) - { - this->value = value; - } - - virtual void accept(ExprVisitor *v) { v->visitClosure(this); } - virtual Closure *asClosure() { return this; } - - virtual void dump(QTextStream &out) const; -}; - -struct Convert: Expr { - Expr *expr; - - void init(Expr *expr, Type type) - { - this->expr = expr; - this->type = type; - } - - virtual void accept(ExprVisitor *v) { v->visitConvert(this); } - virtual Convert *asConvert() { return this; } - - virtual void dump(QTextStream &out) const; -}; - -struct Unop: Expr { - AluOp op; - Expr *expr; - - void init(AluOp op, Expr *expr) - { - this->op = op; - this->expr = expr; - } - - virtual void accept(ExprVisitor *v) { v->visitUnop(this); } - virtual Unop *asUnop() { return this; } - - virtual void dump(QTextStream &out) const; -}; - -struct Binop: Expr { - AluOp op; - Expr *left; // Temp or Const - Expr *right; // Temp or Const - - void init(AluOp op, Expr *left, Expr *right) - { - this->op = op; - this->left = left; - this->right = right; - } - - virtual void accept(ExprVisitor *v) { v->visitBinop(this); } - virtual Binop *asBinop() { return this; } - - virtual void dump(QTextStream &out) const; -}; - -struct Call: Expr { - Expr *base; // Name, Member, Temp - ExprList *args; // List of Temps - - void init(Expr *base, ExprList *args) - { - this->base = base; - this->args = args; - } - - Expr *onlyArgument() const { - if (args && ! args->next) - return args->expr; - return 0; - } - - virtual void accept(ExprVisitor *v) { v->visitCall(this); } - virtual Call *asCall() { return this; } - - virtual void dump(QTextStream &out) const; -}; - -struct New: Expr { - Expr *base; // Name, Member, Temp - ExprList *args; // List of Temps - - void init(Expr *base, ExprList *args) - { - this->base = base; - this->args = args; - } - - Expr *onlyArgument() const { - if (args && ! args->next) - return args->expr; - return 0; - } - - virtual void accept(ExprVisitor *v) { v->visitNew(this); } - virtual New *asNew() { return this; } - - virtual void dump(QTextStream &out) const; -}; - -struct Subscript: Expr { - Expr *base; - Expr *index; - - void init(Expr *base, Expr *index) - { - this->base = base; - this->index = index; - } - - virtual void accept(ExprVisitor *v) { v->visitSubscript(this); } - virtual bool isLValue() { return true; } - virtual Subscript *asSubscript() { return this; } - - virtual void dump(QTextStream &out) const; -}; - -struct Member: Expr { - Expr *base; - const QString *name; - - void init(Expr *base, const QString *name) - { - this->base = base; - this->name = name; - } - - virtual void accept(ExprVisitor *v) { v->visitMember(this); } - virtual bool isLValue() { return true; } - virtual Member *asMember() { return this; } - - virtual void dump(QTextStream &out) const; -}; - -struct Stmt { - enum Mode { - HIR, - MIR - }; - - struct Data { - QVector uses; - QVector defs; - QBitArray liveIn; - QBitArray liveOut; - }; - - Data *d; - int id; - AST::SourceLocation location; - - Stmt(): d(0), id(-1) {} - virtual ~Stmt() { Q_UNREACHABLE(); } - virtual Stmt *asTerminator() { return 0; } - - virtual void accept(StmtVisitor *) = 0; - virtual Exp *asExp() { return 0; } - virtual Move *asMove() { return 0; } - virtual Jump *asJump() { return 0; } - virtual CJump *asCJump() { return 0; } - virtual Ret *asRet() { return 0; } - virtual Try *asTry() { return 0; } - virtual Phi *asPhi() { return 0; } - virtual void dump(QTextStream &out, Mode mode = HIR) = 0; - - void destroyData() { - delete d; - d = 0; - } -}; - -struct Exp: Stmt { - Expr *expr; - - void init(Expr *expr) - { - this->expr = expr; - } - - virtual void accept(StmtVisitor *v) { v->visitExp(this); } - virtual Exp *asExp() { return this; } - - virtual void dump(QTextStream &out, Mode); -}; - -struct Move: Stmt { - Expr *target; // LHS - Temp, Name, Member or Subscript - Expr *source; - AluOp op; - - void init(Expr *target, Expr *source, AluOp op) - { - this->target = target; - this->source = source; - this->op = op; - } - - virtual void accept(StmtVisitor *v) { v->visitMove(this); } - virtual Move *asMove() { return this; } - - virtual void dump(QTextStream &out, Mode); -}; - -struct Jump: Stmt { - BasicBlock *target; - - void init(BasicBlock *target) - { - this->target = target; - } - - virtual Stmt *asTerminator() { return this; } - - virtual void accept(StmtVisitor *v) { v->visitJump(this); } - virtual Jump *asJump() { return this; } - - virtual void dump(QTextStream &out, Mode mode); -}; - -struct CJump: Stmt { - Expr *cond; // Temp, Binop - BasicBlock *iftrue; - BasicBlock *iffalse; - - void init(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse) - { - this->cond = cond; - this->iftrue = iftrue; - this->iffalse = iffalse; - } - - virtual Stmt *asTerminator() { return this; } - - virtual void accept(StmtVisitor *v) { v->visitCJump(this); } - virtual CJump *asCJump() { return this; } - - virtual void dump(QTextStream &out, Mode mode); -}; - -struct Ret: Stmt { - Expr *expr; - - void init(Expr *expr) - { - this->expr = expr; - } - - virtual Stmt *asTerminator() { return this; } - - virtual void accept(StmtVisitor *v) { v->visitRet(this); } - virtual Ret *asRet() { return this; } - - virtual void dump(QTextStream &out, Mode); -}; - -struct Try: Stmt { - BasicBlock *tryBlock; - BasicBlock *catchBlock; - QString exceptionVarName; - Temp *exceptionVar; // place to store the caught exception, for use when re-throwing - - void init(BasicBlock *tryBlock, BasicBlock *catchBlock, const QString &exceptionVarName, Temp *exceptionVar) - { - this->tryBlock = tryBlock; - this->catchBlock = catchBlock; - this->exceptionVarName = exceptionVarName; - this->exceptionVar = exceptionVar; - } - - virtual Stmt *asTerminator() { return this; } - - virtual void accept(StmtVisitor *v) { v->visitTry(this); } - virtual Try *asTry() { return this; } - - virtual void dump(QTextStream &out, Mode mode); -}; - -struct Phi: Stmt { - Temp *targetTemp; - QVector incoming; - - virtual void accept(StmtVisitor *v) { v->visitPhi(this); } - virtual Phi *asPhi() { return this; } - - virtual void dump(QTextStream &out, Mode mode); -}; - -struct Q_QML_EXPORT Module { - MemoryPool pool; - QVector functions; - Function *rootFunction; - - Function *newFunction(const QString &name, Function *outer); - - Module() : rootFunction(0) {} - ~Module(); -}; - -struct Function { - Module *module; - MemoryPool *pool; - const QString *name; - QVector basicBlocks; - int tempCount; - int maxNumberOfArguments; - QSet strings; - QList formals; - QList locals; - QVector nestedFunctions; - Function *outer; - - QString sourceFile; - - int insideWithOrCatch; - - uint hasDirectEval: 1; - uint usesArgumentsObject : 1; - uint isStrict: 1; - uint isNamedExpression : 1; - uint hasTry: 1; - uint hasWith: 1; - uint unused : 26; - - template _Tp *New() { return new (pool->allocate(sizeof(_Tp))) _Tp(); } - - Function(Module *module, Function *outer, const QString &name) - : module(module) - , pool(&module->pool) - , tempCount(0) - , maxNumberOfArguments(0) - , outer(outer) - , insideWithOrCatch(0) - , hasDirectEval(false) - , usesArgumentsObject(false) - , isStrict(false) - , isNamedExpression(false) - , hasTry(false) - , hasWith(false) - , unused(0) - { this->name = newString(name); } - - ~Function(); - - enum BasicBlockInsertMode { - InsertBlock, - DontInsertBlock - }; - - BasicBlock *newBasicBlock(BasicBlock *containingLoop, BasicBlockInsertMode mode = InsertBlock); - const QString *newString(const QString &text); - - void RECEIVE(const QString &name) { formals.append(newString(name)); } - void LOCAL(const QString &name) { locals.append(newString(name)); } - - inline BasicBlock *insertBasicBlock(BasicBlock *block) { basicBlocks.append(block); return block; } - - void dump(QTextStream &out, Stmt::Mode mode = Stmt::HIR); - - void removeSharedExpressions(); - - int indexOfArgument(const QStringRef &string) const; - - bool variablesCanEscape() const - { return hasDirectEval || !nestedFunctions.isEmpty(); } -}; - -struct BasicBlock { - Function *function; - QVector statements; - QVector in; - QVector out; - QBitArray liveIn; - QBitArray liveOut; - int index; - AST::SourceLocation nextLocation; - - BasicBlock(Function *function, BasicBlock *containingLoop) - : function(function) - , index(-1) - , _containingGroup(containingLoop) - , _groupStart(false) - {} - ~BasicBlock() {} - - template inline Instr i(Instr i) { statements.append(i); return i; } - - inline bool isEmpty() const { - return statements.isEmpty(); - } - - inline Stmt *terminator() const { - if (! statements.isEmpty() && statements.at(statements.size() - 1)->asTerminator() != 0) - return statements.at(statements.size() - 1); - return 0; - } - - inline bool isTerminated() const { - if (terminator() != 0) - return true; - return false; - } - - unsigned newTemp(); - - Temp *TEMP(unsigned kind); - Temp *ARG(unsigned index, unsigned scope); - Temp *LOCAL(unsigned index, unsigned scope); - - Expr *CONST(Type type, double value); - Expr *STRING(const QString *value); - Expr *REGEXP(const QString *value, int flags); - - Name *NAME(const QString &id, quint32 line, quint32 column); - Name *NAME(Name::Builtin builtin, quint32 line, quint32 column); - - Name *GLOBALNAME(const QString &id, quint32 line, quint32 column); - - Closure *CLOSURE(Function *function); - - Expr *CONVERT(Expr *expr, Type type); - Expr *UNOP(AluOp op, Expr *expr); - Expr *BINOP(AluOp op, Expr *left, Expr *right); - Expr *CALL(Expr *base, ExprList *args = 0); - Expr *NEW(Expr *base, ExprList *args = 0); - Expr *SUBSCRIPT(Expr *base, Expr *index); - Expr *MEMBER(Expr *base, const QString *name); - - Stmt *EXP(Expr *expr); - - Stmt *MOVE(Expr *target, Expr *source, AluOp op = V4IR::OpInvalid); - - Stmt *JUMP(BasicBlock *target); - Stmt *CJUMP(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse); - Stmt *RET(Temp *expr); - Stmt *TRY(BasicBlock *tryBlock, BasicBlock *catchBlock, const QString &exceptionVarName, Temp *exceptionVar); - - void dump(QTextStream &out, Stmt::Mode mode = Stmt::HIR); - - void appendStatement(Stmt *statement); - - BasicBlock *containingGroup() const - { return _containingGroup; } - - bool isGroupStart() const - { return _groupStart; } - - void markAsGroupStart() - { _groupStart = true; } - -private: - BasicBlock *_containingGroup; - bool _groupStart; -}; - -class CloneExpr: protected V4IR::ExprVisitor -{ -public: - explicit CloneExpr(V4IR::BasicBlock *block = 0); - - void setBasicBlock(V4IR::BasicBlock *block); - - template - _Expr *operator()(_Expr *expr) - { - return clone(expr); - } - - template - _Expr *clone(_Expr *expr) - { - Expr *c = expr; - qSwap(cloned, c); - expr->accept(this); - qSwap(cloned, c); - return static_cast<_Expr *>(c); - } - -protected: - V4IR::ExprList *clone(V4IR::ExprList *list); - - virtual void visitConst(Const *); - virtual void visitString(String *); - virtual void visitRegExp(RegExp *); - virtual void visitName(Name *); - virtual void visitTemp(Temp *); - virtual void visitClosure(Closure *); - virtual void visitConvert(Convert *); - virtual void visitUnop(Unop *); - virtual void visitBinop(Binop *); - virtual void visitCall(Call *); - virtual void visitNew(New *); - virtual void visitSubscript(Subscript *); - virtual void visitMember(Member *); - -private: - V4IR::BasicBlock *block; - V4IR::Expr *cloned; -}; - -} // end of namespace IR - -} // end of namespace QQmlJS - -QT_END_NAMESPACE - -#endif // QV4IR_P_H diff --git a/src/qml/qml/v4/qv4jsonobject.cpp b/src/qml/qml/v4/qv4jsonobject.cpp deleted file mode 100644 index 782c388e5a..0000000000 --- a/src/qml/qml/v4/qv4jsonobject.cpp +++ /dev/null @@ -1,1040 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -using namespace QV4; - -//#define PARSER_DEBUG -#ifdef PARSER_DEBUG -static int indent = 0; -#define BEGIN qDebug() << QByteArray(4*indent++, ' ').constData() -#define END --indent -#define DEBUG qDebug() << QByteArray(4*indent, ' ').constData() -#else -#define BEGIN if (1) ; else qDebug() -#define END do {} while (0) -#define DEBUG if (1) ; else qDebug() -#endif - - -class JsonParser -{ -public: - JsonParser(ExecutionContext *context, const QChar *json, int length); - - Value parse(QJsonParseError *error); - -private: - inline bool eatSpace(); - inline QChar nextToken(); - - Value parseObject(); - Value parseArray(); - bool parseMember(Object *o); - bool parseString(QString *string); - bool parseValue(Value *val); - bool parseNumber(Value *val); - - ExecutionContext *context; - const QChar *head; - const QChar *json; - const QChar *end; - - int nestingLevel; - QJsonParseError::ParseError lastError; -}; - -static const int nestingLimit = 1024; - - -JsonParser::JsonParser(ExecutionContext *context, const QChar *json, int length) - : context(context), head(json), json(json), nestingLevel(0), lastError(QJsonParseError::NoError) -{ - end = json + length; -} - - - -/* - -begin-array = ws %x5B ws ; [ left square bracket - -begin-object = ws %x7B ws ; { left curly bracket - -end-array = ws %x5D ws ; ] right square bracket - -end-object = ws %x7D ws ; } right curly bracket - -name-separator = ws %x3A ws ; : colon - -value-separator = ws %x2C ws ; , comma - -Insignificant whitespace is allowed before or after any of the six -structural characters. - -ws = *( - %x20 / ; Space - %x09 / ; Horizontal tab - %x0A / ; Line feed or New line - %x0D ; Carriage return - ) - -*/ - -enum { - Space = 0x20, - Tab = 0x09, - LineFeed = 0x0a, - Return = 0x0d, - BeginArray = 0x5b, - BeginObject = 0x7b, - EndArray = 0x5d, - EndObject = 0x7d, - NameSeparator = 0x3a, - ValueSeparator = 0x2c, - Quote = 0x22 -}; - -bool JsonParser::eatSpace() -{ - while (json < end) { - if (*json > Space) - break; - if (*json != Space && - *json != Tab && - *json != LineFeed && - *json != Return) - break; - ++json; - } - return (json < end); -} - -QChar JsonParser::nextToken() -{ - if (!eatSpace()) - return 0; - QChar token = *json++; - switch (token.unicode()) { - case BeginArray: - case BeginObject: - case NameSeparator: - case ValueSeparator: - case EndArray: - case EndObject: - eatSpace(); - case Quote: - break; - default: - token = 0; - break; - } - return token; -} - -/* - JSON-text = object / array -*/ -Value JsonParser::parse(QJsonParseError *error) -{ -#ifdef PARSER_DEBUG - indent = 0; - qDebug() << ">>>>> parser begin"; -#endif - - eatSpace(); - - Value v; - if (!parseValue(&v)) { -#ifdef PARSER_DEBUG - qDebug() << ">>>>> parser error"; -#endif - if (lastError == QJsonParseError::NoError) - lastError = QJsonParseError::IllegalValue; - error->offset = json - head; - error->error = lastError; - return Value::undefinedValue(); - } - - // some input left... - if (eatSpace()) { - lastError = QJsonParseError::IllegalValue; - error->offset = json - head; - error->error = lastError; - return Value::undefinedValue(); - } - - END; - error->offset = 0; - error->error = QJsonParseError::NoError; - return v; -} - -/* - object = begin-object [ member *( value-separator member ) ] - end-object -*/ - -Value JsonParser::parseObject() -{ - if (++nestingLevel > nestingLimit) { - lastError = QJsonParseError::DeepNesting; - return Value::undefinedValue(); - } - - BEGIN << "parseObject pos=" << json; - - Object *o = context->engine->newObject(); - Value objectVal = Value::fromObject(o); - - QChar token = nextToken(); - while (token == Quote) { - if (!parseMember(o)) - return Value::undefinedValue(); - token = nextToken(); - if (token != ValueSeparator) - break; - token = nextToken(); - if (token == EndObject) { - lastError = QJsonParseError::MissingObject; - return Value::undefinedValue(); - } - } - - DEBUG << "end token=" << token; - if (token != EndObject) { - lastError = QJsonParseError::UnterminatedObject; - return Value::undefinedValue(); - } - - END; - - --nestingLevel; - return objectVal; -} - -/* - member = string name-separator value -*/ -bool JsonParser::parseMember(Object *o) -{ - BEGIN << "parseMember"; - - QString key; - if (!parseString(&key)) - return false; - QChar token = nextToken(); - if (token != NameSeparator) { - lastError = QJsonParseError::MissingNameSeparator; - return false; - } - Value val; - if (!parseValue(&val)) - return false; - - Property *p = o->insertMember(context->engine->newIdentifier(key), Attr_Data); - p->value = val; - - END; - return true; -} - -/* - array = begin-array [ value *( value-separator value ) ] end-array -*/ -Value JsonParser::parseArray() -{ - BEGIN << "parseArray"; - ArrayObject *array = context->engine->newArrayObject(); - - if (++nestingLevel > nestingLimit) { - lastError = QJsonParseError::DeepNesting; - return Value::undefinedValue(); - } - - if (!eatSpace()) { - lastError = QJsonParseError::UnterminatedArray; - return Value::undefinedValue(); - } - if (*json == EndArray) { - nextToken(); - } else { - uint index = 0; - while (1) { - Value val; - if (!parseValue(&val)) - return Value::undefinedValue(); - array->arraySet(index, val); - QChar token = nextToken(); - if (token == EndArray) - break; - else if (token != ValueSeparator) { - if (!eatSpace()) - lastError = QJsonParseError::UnterminatedArray; - else - lastError = QJsonParseError::MissingValueSeparator; - return Value::undefinedValue(); - } - ++index; - } - } - - DEBUG << "size =" << array->arrayLength(); - END; - - --nestingLevel; - return Value::fromObject(array); -} - -/* -value = false / null / true / object / array / number / string - -*/ - -bool JsonParser::parseValue(Value *val) -{ - BEGIN << "parse Value" << *json; - - switch ((json++)->unicode()) { - case 'n': - if (end - json < 3) { - lastError = QJsonParseError::IllegalValue; - return false; - } - if (*json++ == 'u' && - *json++ == 'l' && - *json++ == 'l') { - *val = Value::nullValue(); - DEBUG << "value: null"; - END; - return true; - } - lastError = QJsonParseError::IllegalValue; - return false; - case 't': - if (end - json < 3) { - lastError = QJsonParseError::IllegalValue; - return false; - } - if (*json++ == 'r' && - *json++ == 'u' && - *json++ == 'e') { - *val = Value::fromBoolean(true); - DEBUG << "value: true"; - END; - return true; - } - lastError = QJsonParseError::IllegalValue; - return false; - case 'f': - if (end - json < 4) { - lastError = QJsonParseError::IllegalValue; - return false; - } - if (*json++ == 'a' && - *json++ == 'l' && - *json++ == 's' && - *json++ == 'e') { - *val = Value::fromBoolean(false); - DEBUG << "value: false"; - END; - return true; - } - lastError = QJsonParseError::IllegalValue; - return false; - case Quote: { - QString value; - if (!parseString(&value)) - return false; - DEBUG << "value: string"; - END; - *val = Value::fromString(context, value); - return true; - } - case BeginArray: { - *val = parseArray(); - if (val->isUndefined()) - return false; - DEBUG << "value: array"; - END; - return true; - } - case BeginObject: { - *val = parseObject(); - if (val->isUndefined()) - return false; - DEBUG << "value: object"; - END; - return true; - } - case EndArray: - lastError = QJsonParseError::MissingObject; - return false; - default: - --json; - if (!parseNumber(val)) - return false; - DEBUG << "value: number"; - END; - } - - return true; -} - - - - - -/* - number = [ minus ] int [ frac ] [ exp ] - decimal-point = %x2E ; . - digit1-9 = %x31-39 ; 1-9 - e = %x65 / %x45 ; e E - exp = e [ minus / plus ] 1*DIGIT - frac = decimal-point 1*DIGIT - int = zero / ( digit1-9 *DIGIT ) - minus = %x2D ; - - plus = %x2B ; + - zero = %x30 ; 0 - -*/ - -bool JsonParser::parseNumber(Value *val) -{ - BEGIN << "parseNumber" << *json; - - const QChar *start = json; - bool isInt = true; - - // minus - if (json < end && *json == '-') - ++json; - - // int = zero / ( digit1-9 *DIGIT ) - if (json < end && *json == '0') { - ++json; - } else { - while (json < end && *json >= '0' && *json <= '9') - ++json; - } - - // frac = decimal-point 1*DIGIT - if (json < end && *json == '.') { - isInt = false; - ++json; - while (json < end && *json >= '0' && *json <= '9') - ++json; - } - - // exp = e [ minus / plus ] 1*DIGIT - if (json < end && (*json == 'e' || *json == 'E')) { - isInt = false; - ++json; - if (json < end && (*json == '-' || *json == '+')) - ++json; - while (json < end && *json >= '0' && *json <= '9') - ++json; - } - - QString number(start, json - start); - DEBUG << "numberstring" << number; - - if (isInt) { - bool ok; - int n = number.toInt(&ok); - if (ok && n < (1<<25) && n > -(1<<25)) { - *val = Value::fromInt32(n); - END; - return true; - } - } - - bool ok; - double d; - d = number.toDouble(&ok); - - if (!ok) { - lastError = QJsonParseError::IllegalNumber; - return false; - } - - * val = Value::fromDouble(d); - - END; - return true; -} - -/* - - string = quotation-mark *char quotation-mark - - char = unescaped / - escape ( - %x22 / ; " quotation mark U+0022 - %x5C / ; \ reverse solidus U+005C - %x2F / ; / solidus U+002F - %x62 / ; b backspace U+0008 - %x66 / ; f form feed U+000C - %x6E / ; n line feed U+000A - %x72 / ; r carriage return U+000D - %x74 / ; t tab U+0009 - %x75 4HEXDIG ) ; uXXXX U+XXXX - - escape = %x5C ; \ - - quotation-mark = %x22 ; " - - unescaped = %x20-21 / %x23-5B / %x5D-10FFFF - */ -static inline bool addHexDigit(QChar digit, uint *result) -{ - ushort d = digit.unicode(); - *result <<= 4; - if (d >= '0' && d <= '9') - *result |= (d - '0'); - else if (d >= 'a' && d <= 'f') - *result |= (d - 'a') + 10; - else if (d >= 'A' && d <= 'F') - *result |= (d - 'A') + 10; - else - return false; - return true; -} - -static inline bool scanEscapeSequence(const QChar *&json, const QChar *end, uint *ch) -{ - ++json; - if (json >= end) - return false; - - DEBUG << "scan escape"; - uint escaped = (json++)->unicode(); - switch (escaped) { - case '"': - *ch = '"'; break; - case '\\': - *ch = '\\'; break; - case '/': - *ch = '/'; break; - case 'b': - *ch = 0x8; break; - case 'f': - *ch = 0xc; break; - case 'n': - *ch = 0xa; break; - case 'r': - *ch = 0xd; break; - case 't': - *ch = 0x9; break; - case 'u': { - *ch = 0; - if (json > end - 4) - return false; - for (int i = 0; i < 4; ++i) { - if (!addHexDigit(*json, ch)) - return false; - ++json; - } - if (*ch <= 0x1f) - return false; - return true; - } - default: - return false; - } - return true; -} - - -bool JsonParser::parseString(QString *string) -{ - BEGIN << "parse string stringPos=" << json; - - while (json < end) { - if (*json == '"') - break; - else if (*json == '\\') { - uint ch = 0; - if (!scanEscapeSequence(json, end, &ch)) { - lastError = QJsonParseError::IllegalEscapeSequence; - return false; - } - qDebug() << "scanEscape" << hex << ch; - if (QChar::requiresSurrogates(ch)) { - *string += QChar::highSurrogate(ch); - *string += QChar::lowSurrogate(ch); - } else { - *string += QChar(ch); - } - } else { - if (json->unicode() <= 0x1f) { - lastError = QJsonParseError::IllegalEscapeSequence; - return false; - } - *string += *json; - ++json; - } - } - ++json; - - if (json > end) { - lastError = QJsonParseError::UnterminatedString; - return false; - } - - END; - return true; -} - - -struct Stringify -{ - ExecutionContext *ctx; - FunctionObject *replacerFunction; - QVector propertyList; - QString gap; - QString indent; - - QStack stack; - - Stringify(ExecutionContext *ctx) : ctx(ctx), replacerFunction(0) {} - - QString Str(const QString &key, Value value); - QString JA(ArrayObject *a); - QString JO(Object *o); - - QString makeMember(const QString &key, Value v); -}; - -static QString quote(const QString &str) -{ - QString product = "\""; - for (int i = 0; i < str.length(); ++i) { - QChar c = str.at(i); - switch (c.unicode()) { - case '"': - product += "\\\""; - break; - case '\\': - product += "\\\\"; - break; - case '\b': - product += "\\b"; - break; - case '\f': - product += "\\f"; - break; - case '\n': - product += "\\n"; - break; - case '\r': - product += "\\r"; - break; - case '\t': - product += "\\t"; - break; - default: - if (c.unicode() <= 0x1f) { - product += "\\u00"; - product += c.unicode() > 0xf ? '1' : '0'; - product += "0123456789abcdef"[c.unicode() & 0xf]; - } else { - product += c; - } - } - } - product += '"'; - return product; -} - -QString Stringify::Str(const QString &key, Value value) -{ - QString result; - - if (Object *o = value.asObject()) { - FunctionObject *toJSON = o->get(ctx->engine->newString(QStringLiteral("toJSON"))).asFunctionObject(); - if (toJSON) { - Value arg = Value::fromString(ctx, key); - value = toJSON->call(value, &arg, 1); - } - } - - if (replacerFunction) { - Object *holder = ctx->engine->newObject(); - Value holderValue = Value::fromObject(holder); - holder->put(ctx, QString(), value); - Value args[2]; - args[0] = Value::fromString(ctx, key); - args[1] = value; - value = replacerFunction->call(holderValue, args, 2); - } - - if (Object *o = value.asObject()) { - if (NumberObject *n = o->asNumberObject()) - value = n->value; - else if (StringObject *so = o->asStringObject()) - value = so->value; - else if (BooleanObject *b =o->asBooleanObject()) - value = b->value; - } - - if (value.isNull()) - return QStringLiteral("null"); - if (value.isBoolean()) - return value.booleanValue() ? QStringLiteral("true") : QStringLiteral("false"); - if (value.isString()) - return quote(value.stringValue()->toQString()); - - if (value.isNumber()) { - double d = value.toNumber(); - return std::isfinite(d) ? value.toString(ctx)->toQString() : QStringLiteral("null"); - } - - if (Object *o = value.asObject()) { - if (!o->asFunctionObject()) { - if (o->asArrayObject()) - return JA(static_cast(o)); - else - return JO(o); - } - } - - return QString(); -} - -QString Stringify::makeMember(const QString &key, Value v) -{ - QString strP = Str(key, v); - if (!strP.isEmpty()) { - QString member = quote(key) + ':'; - if (!gap.isEmpty()) - member += ' '; - member += strP; - return member; - } - return QString(); -} - -QString Stringify::JO(Object *o) -{ - if (stack.contains(o)) - ctx->throwTypeError(); - - QString result; - stack.push(o); - QString stepback = indent; - indent += gap; - - QStringList partial; - if (propertyList.isEmpty()) { - ObjectIterator it(o, ObjectIterator::EnumerableOnly); - - while (1) { - Value v; - Value name = it.nextPropertyNameAsString(&v); - if (name.isNull()) - break; - QString key = name.toQString(); - QString member = makeMember(key, v); - if (!member.isEmpty()) - partial += member; - } - } else { - for (int i = 0; i < propertyList.size(); ++i) { - bool exists; - Value v = o->get(propertyList.at(i), &exists); - if (!exists) - continue; - QString member = makeMember(propertyList.at(i)->toQString(), v); - if (!member.isEmpty()) - partial += member; - } - } - - if (partial.isEmpty()) { - result = QStringLiteral("{}"); - } else if (gap.isEmpty()) { - result = "{" + partial.join(",") + "}"; - } else { - QString separator = ",\n" + indent; - result = "{\n" + indent + partial.join(separator) + "\n" + stepback + "}"; - } - - indent = stepback; - stack.pop(); - return result; -} - -QString Stringify::JA(ArrayObject *a) -{ - if (stack.contains(a)) - ctx->throwTypeError(); - - QString result; - stack.push(a); - QString stepback = indent; - indent += gap; - - QStringList partial; - uint len = a->arrayLength(); - for (uint i = 0; i < len; ++i) { - bool exists; - Value v = a->getIndexed(i, &exists); - if (!exists) { - partial += QStringLiteral("null"); - continue; - } - QString strP = Str(QString::number(i), v); - if (!strP.isEmpty()) - partial += strP; - else - partial += QStringLiteral("null"); - } - - if (partial.isEmpty()) { - result = QStringLiteral("[]"); - } else if (gap.isEmpty()) { - result = "[" + partial.join(",") + "]"; - } else { - QString separator = ",\n" + indent; - result = "[\n" + indent + partial.join(separator) + "\n" + stepback + "]"; - } - - indent = stepback; - stack.pop(); - return result; -} - - -JsonObject::JsonObject(ExecutionContext *context) - : Object(context->engine) -{ - type = Type_JSONObject; - prototype = context->engine->objectPrototype; - - defineDefaultProperty(context, QStringLiteral("parse"), method_parse, 2); - defineDefaultProperty(context, QStringLiteral("stringify"), method_stringify, 3); -} - - -Value JsonObject::method_parse(SimpleCallContext *ctx) -{ - QString jtext = ctx->argument(0).toString(ctx)->toQString(); - - DEBUG << "parsing source = " << jtext; - JsonParser parser(ctx, jtext.constData(), jtext.length()); - QJsonParseError error; - Value result = parser.parse(&error); - if (error.error != QJsonParseError::NoError) { - DEBUG << "parse error" << error.errorString(); - ctx->throwSyntaxError(0); - } - - return result; -} - -Value JsonObject::method_stringify(SimpleCallContext *ctx) -{ - Stringify stringify(ctx); - - Object *o = ctx->argument(1).asObject(); - if (o) { - stringify.replacerFunction = o->asFunctionObject(); - if (o->isArrayObject()) { - uint arrayLen = o->arrayLength(); - for (uint i = 0; i < arrayLen; ++i) { - Value v = o->getIndexed(i); - if (v.asNumberObject() || v.asStringObject() || v.isNumber()) - v = __qmljs_to_string(v, ctx); - if (v.isString()) { - String *s = v.stringValue(); - if (!stringify.propertyList.contains(s)) - stringify.propertyList.append(s); - } - } - } - } - - Value s = ctx->argument(2); - if (NumberObject *n = s.asNumberObject()) - s = n->value; - else if (StringObject *so = s.asStringObject()) - s = so->value; - - if (s.isNumber()) { - stringify.gap = QString(qMin(10, (int)s.toInteger()), ' '); - } else if (s.isString()) { - stringify.gap = s.stringValue()->toQString().left(10); - } - - - QString result = stringify.Str(QString(), ctx->argument(0)); - if (result.isEmpty()) - return Value::undefinedValue(); - return Value::fromString(ctx, result); -} - - - -QV4::Value JsonObject::fromJsonValue(ExecutionEngine *engine, const QJsonValue &value) -{ - if (value.isString()) - return Value::fromString(engine->current, value.toString()); - else if (value.isDouble()) - return Value::fromDouble(value.toDouble()); - else if (value.isBool()) - return Value::fromBoolean(value.toBool()); - else if (value.isArray()) - return fromJsonArray(engine, value.toArray()); - else if (value.isObject()) - return fromJsonObject(engine, value.toObject()); - else if (value.isNull()) - return Value::nullValue(); - else - return Value::undefinedValue(); -} - -QJsonValue JsonObject::toJsonValue(const QV4::Value &value, - V4ObjectSet &visitedObjects) -{ - if (String *s = value.asString()) - return QJsonValue(s->toQString()); - else if (value.isNumber()) - return QJsonValue(value.toNumber()); - else if (value.isBoolean()) - return QJsonValue((bool)value.booleanValue()); - else if (ArrayObject *a = value.asArrayObject()) - return toJsonArray(a, visitedObjects); - else if (Object *o = value.asObject()) - return toJsonObject(o, visitedObjects); - else if (value.isNull()) - return QJsonValue(QJsonValue::Null); - else - return QJsonValue(QJsonValue::Undefined); -} - -QV4::Value JsonObject::fromJsonObject(ExecutionEngine *engine, const QJsonObject &object) -{ - Object *o = engine->newObject(); - for (QJsonObject::const_iterator it = object.begin(); it != object.end(); ++it) - o->put(engine->newString(it.key()), fromJsonValue(engine, it.value())); - return Value::fromObject(o); -} - -QJsonObject JsonObject::toJsonObject(QV4::Object *o, V4ObjectSet &visitedObjects) -{ - QJsonObject result; - if (!o || o->asFunctionObject()) - return result; - - if (visitedObjects.contains(o)) { - // Avoid recursion. - // For compatibility with QVariant{List,Map} conversion, we return an - // empty object (and no error is thrown). - return result; - } - - visitedObjects.insert(o); - - ObjectIterator it(o, ObjectIterator::EnumerableOnly); - while (1) { - Value v; - Value name = it.nextPropertyNameAsString(&v); - if (name.isNull()) - break; - - QString key = name.toQString(); - if (!v.asFunctionObject()) - result.insert(key, toJsonValue(v, visitedObjects)); - } - - visitedObjects.remove(o); - - return result; -} - -QV4::Value JsonObject::fromJsonArray(ExecutionEngine *engine, const QJsonArray &array) -{ - int size = array.size(); - ArrayObject *a = engine->newArrayObject(); - a->arrayReserve(size); - a->arrayDataLen = size; - for (int i = 0; i < size; i++) - a->arrayData[i].value = fromJsonValue(engine, array.at(i)); - a->setArrayLengthUnchecked(size); - return Value::fromObject(a); -} - -QJsonArray JsonObject::toJsonArray(ArrayObject *a, V4ObjectSet &visitedObjects) -{ - QJsonArray result; - if (!a) - return result; - - if (visitedObjects.contains(a)) { - // Avoid recursion. - // For compatibility with QVariant{List,Map} conversion, we return an - // empty array (and no error is thrown). - return result; - } - - visitedObjects.insert(a); - - quint32 length = a->arrayLength(); - for (quint32 i = 0; i < length; ++i) { - Value v = a->getIndexed(i); - result.append(toJsonValue(v.asFunctionObject() ? QV4::Value::nullValue() : v, visitedObjects)); - } - - visitedObjects.remove(a); - - return result; -} diff --git a/src/qml/qml/v4/qv4jsonobject_p.h b/src/qml/qml/v4/qv4jsonobject_p.h deleted file mode 100644 index ccd99d5488..0000000000 --- a/src/qml/qml/v4/qv4jsonobject_p.h +++ /dev/null @@ -1,85 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QV4JSONOBJECTS_H -#define QV4SJONOBJECTS_H - -#include "qv4object_p.h" -#include -#include -#include - -QT_BEGIN_NAMESPACE - -namespace QV4 { - -struct JsonObject : Object { -private: - typedef QSet V4ObjectSet; -public: - JsonObject(ExecutionContext *context); - - static Value method_parse(SimpleCallContext *ctx); - static Value method_stringify(SimpleCallContext *ctx); - - static QV4::Value fromJsonValue(ExecutionEngine *engine, const QJsonValue &value); - static QV4::Value fromJsonObject(ExecutionEngine *engine, const QJsonObject &object); - static QV4::Value fromJsonArray(ExecutionEngine *engine, const QJsonArray &array); - - static inline QJsonValue toJsonValue(const QV4::Value &value) - { V4ObjectSet visitedObjects; return toJsonValue(value, visitedObjects); } - static inline QJsonObject toJsonObject(QV4::Object *o) - { V4ObjectSet visitedObjects; return toJsonObject(o, visitedObjects); } - static inline QJsonArray toJsonArray(QV4::ArrayObject *a) - { V4ObjectSet visitedObjects; return toJsonArray(a, visitedObjects); } - -private: - static QJsonValue toJsonValue(const QV4::Value &value, V4ObjectSet &visitedObjects); - static QJsonObject toJsonObject(QV4::Object *o, V4ObjectSet &visitedObjects); - static QJsonArray toJsonArray(QV4::ArrayObject *a, V4ObjectSet &visitedObjects); - -}; - -} - -QT_END_NAMESPACE - -#endif - diff --git a/src/qml/qml/v4/qv4lookup.cpp b/src/qml/qml/v4/qv4lookup.cpp deleted file mode 100644 index b5ea877bd4..0000000000 --- a/src/qml/qml/v4/qv4lookup.cpp +++ /dev/null @@ -1,365 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#include "qv4lookup_p.h" -#include "qv4functionobject_p.h" - -QT_BEGIN_NAMESPACE - -using namespace QV4; - -Property *Lookup::lookup(Object *obj, PropertyAttributes *attrs) -{ - int i = 0; - while (i < level && obj && obj->internalClass == classList[i]) { - obj = obj->prototype; - ++i; - } - - if (index != UINT_MAX && obj->internalClass == classList[i]) { - *attrs = obj->internalClass->propertyData.at(index); - return obj->memberData + index; - } - - while (i < Size && obj) { - classList[i] = obj->internalClass; - - index = obj->internalClass->find(name); - if (index != UINT_MAX) { - level = i; - *attrs = obj->internalClass->propertyData.at(index); - return obj->memberData + index; - } - - obj = obj->prototype; - ++i; - } - level = i; - - while (obj) { - index = obj->internalClass->find(name); - if (index != UINT_MAX) { - *attrs = obj->internalClass->propertyData.at(index); - return obj->memberData + index; - } - - obj = obj->prototype; - } - return 0; -} - - -void Lookup::getterGeneric(QV4::Lookup *l, QV4::Value *result, const QV4::Value &object) -{ - if (Object *o = object.asObject()) { - o->getLookup(l, result); - return; - } - - Value res; - if (Managed *m = object.asManaged()) { - res = m->get(l->name); - } else { - ExecutionContext *ctx = l->name->engine()->current; - Object *o = __qmljs_convert_to_object(ctx, object); - res = o->get(l->name); - } - if (result) - *result = res; -} - -void Lookup::getter0(Lookup *l, Value *result, const Value &object) -{ - if (Object *o = object.asObject()) { - if (l->classList[0] == o->internalClass) { - if (result) - *result = o->memberData[l->index].value; - return; - } - } - l->getter = getterGeneric; - getterGeneric(l, result, object); -} - -void Lookup::getter1(Lookup *l, Value *result, const Value &object) -{ - if (Object *o = object.asObject()) { - if (l->classList[0] == o->internalClass && - l->classList[1] == o->prototype->internalClass) { - if (result) - *result = o->prototype->memberData[l->index].value; - return; - } - } - l->getter = getterGeneric; - getterGeneric(l, result, object); -} - -void Lookup::getter2(Lookup *l, Value *result, const Value &object) -{ - if (Object *o = object.asObject()) { - if (l->classList[0] == o->internalClass) { - o = o->prototype; - if (l->classList[1] == o->internalClass) { - o = o->prototype; - if (l->classList[2] == o->internalClass) { - if (result) - *result = o->memberData[l->index].value; - return; - } - } - } - } - l->getter = getterGeneric; - getterGeneric(l, result, object); -} - -void Lookup::getterAccessor0(Lookup *l, Value *result, const Value &object) -{ - if (Object *o = object.asObject()) { - if (l->classList[0] == o->internalClass) { - Value res; - FunctionObject *getter = o->memberData[l->index].getter(); - if (!getter) - res = Value::undefinedValue(); - else - res = getter->call(object, 0, 0); - if (result) - *result = res; - return; - } - } - l->getter = getterGeneric; - getterGeneric(l, result, object); -} - -void Lookup::getterAccessor1(Lookup *l, Value *result, const Value &object) -{ - if (Object *o = object.asObject()) { - if (l->classList[0] == o->internalClass && - l->classList[1] == o->prototype->internalClass) { - Value res; - FunctionObject *getter = o->prototype->memberData[l->index].getter(); - if (!getter) - res = Value::undefinedValue(); - else - res = getter->call(object, 0, 0); - if (result) - *result = res; - return; - } - } - l->getter = getterGeneric; - getterGeneric(l, result, object); -} - -void Lookup::getterAccessor2(Lookup *l, Value *result, const Value &object) -{ - if (Object *o = object.asObject()) { - if (l->classList[0] == o->internalClass) { - o = o->prototype; - if (l->classList[1] == o->internalClass) { - o = o->prototype; - if (l->classList[2] == o->internalClass) { - Value res; - FunctionObject *getter = o->memberData[l->index].getter(); - if (!getter) - res = Value::undefinedValue(); - else - res = getter->call(object, 0, 0); - if (result) - *result = res; - return; - } - } - } - } - l->getter = getterGeneric; - getterGeneric(l, result, object); -} - - -void Lookup::globalGetterGeneric(Lookup *l, ExecutionContext *ctx, Value *result) -{ - Object *o = ctx->engine->globalObject; - PropertyAttributes attrs; - Property *p = l->lookup(o, &attrs); - if (p) { - if (attrs.isData()) { - if (l->level == 0) - l->globalGetter = globalGetter0; - else if (l->level == 1) - l->globalGetter = globalGetter1; - else if (l->level == 2) - l->globalGetter = globalGetter2; - *result = p->value; - return; - } else { - if (l->level == 0) - l->globalGetter = globalGetterAccessor0; - else if (l->level == 1) - l->globalGetter = globalGetterAccessor1; - else if (l->level == 2) - l->globalGetter = globalGetterAccessor2; - Value res = o->getValue(p, attrs); - if (result) - *result = res; - return; - } - } - ctx->throwReferenceError(Value::fromString(l->name)); -} - -void Lookup::globalGetter0(Lookup *l, ExecutionContext *ctx, Value *result) -{ - Object *o = ctx->engine->globalObject; - if (l->classList[0] == o->internalClass) { - *result = o->memberData[l->index].value; - return; - } - l->globalGetter = globalGetterGeneric; - globalGetterGeneric(l, ctx, result); -} - -void Lookup::globalGetter1(Lookup *l, ExecutionContext *ctx, Value *result) -{ - Object *o = ctx->engine->globalObject; - if (l->classList[0] == o->internalClass && - l->classList[1] == o->prototype->internalClass) { - *result = o->prototype->memberData[l->index].value; - return; - } - l->globalGetter = globalGetterGeneric; - globalGetterGeneric(l, ctx, result); -} - -void Lookup::globalGetter2(Lookup *l, ExecutionContext *ctx, Value *result) -{ - Object *o = ctx->engine->globalObject; - if (l->classList[0] == o->internalClass) { - o = o->prototype; - if (l->classList[1] == o->internalClass) { - o = o->prototype; - if (l->classList[2] == o->internalClass) { - *result = o->prototype->memberData[l->index].value; - return; - } - } - } - l->globalGetter = globalGetterGeneric; - globalGetterGeneric(l, ctx, result); -} - -void Lookup::globalGetterAccessor0(Lookup *l, ExecutionContext *ctx, Value *result) -{ - Object *o = ctx->engine->globalObject; - if (l->classList[0] == o->internalClass) { - FunctionObject *getter = o->memberData[l->index].getter(); - if (!getter) - *result = Value::undefinedValue(); - else - *result = getter->call(Value::undefinedValue(), 0, 0); - return; - } - l->globalGetter = globalGetterGeneric; - globalGetterGeneric(l, ctx, result); -} - -void Lookup::globalGetterAccessor1(Lookup *l, ExecutionContext *ctx, Value *result) -{ - Object *o = ctx->engine->globalObject; - if (l->classList[0] == o->internalClass && - l->classList[1] == o->prototype->internalClass) { - FunctionObject *getter = o->prototype->memberData[l->index].getter(); - if (!getter) - *result = Value::undefinedValue(); - else - *result = getter->call(Value::undefinedValue(), 0, 0); - return; - } - l->globalGetter = globalGetterGeneric; - globalGetterGeneric(l, ctx, result); -} - -void Lookup::globalGetterAccessor2(Lookup *l, ExecutionContext *ctx, Value *result) -{ - Object *o = ctx->engine->globalObject; - if (l->classList[0] == o->internalClass) { - o = o->prototype; - if (l->classList[1] == o->internalClass) { - o = o->prototype; - if (l->classList[2] == o->internalClass) { - FunctionObject *getter = o->memberData[l->index].getter(); - if (!getter) - *result = Value::undefinedValue(); - else - *result = getter->call(Value::undefinedValue(), 0, 0); - return; - } - } - } - l->globalGetter = globalGetterGeneric; - globalGetterGeneric(l, ctx, result); -} - -void Lookup::setterGeneric(Lookup *l, const Value &object, const Value &value) -{ - Object *o = object.asObject(); - if (!o) { - o = __qmljs_convert_to_object(l->name->engine()->current, object); - o->put(l->name, value); - return; - } - o->setLookup(l, value); -} - -void Lookup::setter0(Lookup *l, const Value &object, const Value &value) -{ - Object *o = object.asObject(); - if (o && o->internalClass == l->classList[0]) { - o->memberData[l->index].value = value; - return; - } - - l->setter = setterGeneric; - setterGeneric(l, object, value); -} - -QT_END_NAMESPACE diff --git a/src/qml/qml/v4/qv4lookup_p.h b/src/qml/qml/v4/qv4lookup_p.h deleted file mode 100644 index e77552826a..0000000000 --- a/src/qml/qml/v4/qv4lookup_p.h +++ /dev/null @@ -1,94 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QV4LOOKUP_H -#define QV4LOOKUP_H - -#include "qv4global_p.h" -#include "qv4runtime_p.h" -#include "qv4engine_p.h" -#include "qv4context_p.h" -#include "qv4object_p.h" -#include "qv4internalclass_p.h" - -QT_BEGIN_NAMESPACE - -namespace QV4 { - -struct Lookup { - enum { Size = 3 }; - union { - void (*getter)(Lookup *l, Value *result, const Value &object); - void (*globalGetter)(Lookup *l, ExecutionContext *ctx, Value *result); - void (*setter)(Lookup *l, const Value &object, const Value &v); - }; - InternalClass *classList[Size]; - int level; - uint index; - String *name; - - static void getterGeneric(Lookup *l, Value *result, const Value &object); - static void getter0(Lookup *l, Value *result, const Value &object); - static void getter1(Lookup *l, Value *result, const Value &object); - static void getter2(Lookup *l, Value *result, const Value &object); - static void getterAccessor0(Lookup *l, Value *result, const Value &object); - static void getterAccessor1(Lookup *l, Value *result, const Value &object); - static void getterAccessor2(Lookup *l, Value *result, const Value &object); - - static void globalGetterGeneric(Lookup *l, ExecutionContext *ctx, Value *result); - static void globalGetter0(Lookup *l, ExecutionContext *ctx, Value *result); - static void globalGetter1(Lookup *l, ExecutionContext *ctx, Value *result); - static void globalGetter2(Lookup *l, ExecutionContext *ctx, Value *result); - static void globalGetterAccessor0(Lookup *l, ExecutionContext *ctx, Value *result); - static void globalGetterAccessor1(Lookup *l, ExecutionContext *ctx, Value *result); - static void globalGetterAccessor2(Lookup *l, ExecutionContext *ctx, Value *result); - - static void setterGeneric(Lookup *l, const Value &object, const Value &value); - static void setter0(Lookup *l, const Value &object, const Value &value); - - Property *lookup(Object *obj, PropertyAttributes *attrs); - -}; - -} - -QT_END_NAMESPACE - -#endif diff --git a/src/qml/qml/v4/qv4managed.cpp b/src/qml/qml/v4/qv4managed.cpp deleted file mode 100644 index 19adb354e3..0000000000 --- a/src/qml/qml/v4/qv4managed.cpp +++ /dev/null @@ -1,212 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4managed_p.h" -#include "qv4mm_p.h" -#include "qv4errorobject_p.h" - -using namespace QV4; - -const ManagedVTable Managed::static_vtbl = -{ - call, - construct, - 0 /*markObjects*/, - destroy, - 0 /*collectDeletables*/, - hasInstance, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - isEqualTo, - 0, - "Managed", -}; - - -void *Managed::operator new(size_t size, MemoryManager *mm) -{ - assert(mm); - - return mm->allocManaged(size); -} - -void Managed::operator delete(void *ptr) -{ - if (!ptr) - return; - - Managed *m = static_cast(ptr); - m->vtbl = 0; - m->_data = 0; - m->~Managed(); -} - -void Managed::operator delete(void *ptr, MemoryManager *mm) -{ - Q_UNUSED(mm); - - operator delete(ptr); -} - -ExecutionEngine *Managed::engine() const -{ - return internalClass ? internalClass->engine : 0; -} - -QString Managed::className() const -{ - const char *s = 0; - switch (Type(type)) { - case Type_Invalid: - case Type_String: - return QString(); - case Type_Object: - s = "Object"; - break; - case Type_ArrayObject: - s = "Array"; - break; - case Type_FunctionObject: - s = "Function"; - break; - case Type_BooleanObject: - s = "Boolean"; - break; - case Type_NumberObject: - s = "Number"; - break; - case Type_StringObject: - s = "String"; - break; - case Type_DateObject: - s = "Date"; - break; - case Type_RegExpObject: - s = "RegExp"; - break; - case Type_ErrorObject: - switch (ErrorObject::ErrorType(subtype)) { - case ErrorObject::Error: - s = "Error"; - break; - case ErrorObject::EvalError: - s = "EvalError"; - break; - case ErrorObject::RangeError: - s = "RangeError"; - break; - case ErrorObject::ReferenceError: - s = "ReferenceError"; - break; - case ErrorObject::SyntaxError: - s = "SyntaxError"; - break; - case ErrorObject::TypeError: - s = "TypeError"; - break; - case ErrorObject::URIError: - s = "URIError"; - break; - } - break; - case Type_ArgumentsObject: - s = "Arguments"; - break; - case Type_JSONObject: - s = "JSON"; - break; - case Type_MathObject: - s = "Math"; - break; - case Type_ForeachIteratorObject: - s = "__ForeachIterator"; - break; - } - return QString::fromLatin1(s); -} - -bool Managed::hasInstance(Managed *m, const Value &) -{ - m->engine()->current->throwTypeError(); -} - -Value Managed::construct(Managed *m, Value *, int) -{ - m->engine()->current->throwTypeError(); -} - -Value Managed::call(Managed *m, const Value &, Value *, int) -{ - m->engine()->current->throwTypeError(); -} - -void Managed::getLookup(Managed *m, Lookup *, Value *) -{ - m->engine()->current->throwTypeError(); -} - -void Managed::setLookup(Managed *m, Lookup *, const Value &) -{ - m->engine()->current->throwTypeError(); -} - -bool Managed::isEqualTo(Managed *, Managed *) -{ - return false; -} - -Value Managed::get(String *name, bool *hasProperty) -{ - return vtbl->get(this, name, hasProperty); -} - -Value Managed::getIndexed(uint index, bool *hasProperty) -{ - return vtbl->getIndexed(this, index, hasProperty); -} diff --git a/src/qml/qml/v4/qv4managed_p.h b/src/qml/qml/v4/qv4managed_p.h deleted file mode 100644 index 5e3c142bb8..0000000000 --- a/src/qml/qml/v4/qv4managed_p.h +++ /dev/null @@ -1,321 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QMLJS_MANAGED_H -#define QMLJS_MANAGED_H - -#include -#include -#include -#include "qv4global_p.h" -#include "qv4value_def_p.h" - -QT_BEGIN_NAMESPACE - -namespace QV4 { - -#define Q_MANAGED_CHECK \ - template inline void qt_check_for_QMANAGED_macro(const T &_q_argument) const \ - { int i = qYouForgotTheQ_MANAGED_Macro(this, &_q_argument); i = i + 1; } - -template -inline int qYouForgotTheQ_MANAGED_Macro(T, T) { return 0; } - -template -inline void qYouForgotTheQ_MANAGED_Macro(T1, T2) {} - -#define Q_MANAGED \ - public: \ - Q_MANAGED_CHECK \ - static const QV4::ManagedVTable static_vtbl; - -struct GCDeletable -{ - GCDeletable() : next(0), lastCall(false) {} - virtual ~GCDeletable() {} - GCDeletable *next; - bool lastCall; -}; - -struct ManagedVTable -{ - Value (*call)(Managed *, const Value &thisObject, Value *args, int argc); - Value (*construct)(Managed *, Value *args, int argc); - void (*markObjects)(Managed *); - void (*destroy)(Managed *); - void (*collectDeletables)(Managed *, GCDeletable **deletable); - bool (*hasInstance)(Managed *, const Value &value); - Value (*get)(Managed *, String *name, bool *hasProperty); - Value (*getIndexed)(Managed *, uint index, bool *hasProperty); - void (*put)(Managed *, String *name, const Value &value); - void (*putIndexed)(Managed *, uint index, const Value &value); - PropertyAttributes (*query)(const Managed *, String *name); - PropertyAttributes (*queryIndexed)(const Managed *, uint index); - bool (*deleteProperty)(Managed *m, String *name); - bool (*deleteIndexedProperty)(Managed *m, uint index); - void (*getLookup)(Managed *m, Lookup *l, Value *result); - void (*setLookup)(Managed *m, Lookup *l, const Value &v); - bool (*isEqualTo)(Managed *m, Managed *other); - Property *(*advanceIterator)(Managed *m, ObjectIterator *it, String **name, uint *index, PropertyAttributes *attributes); - const char *className; -}; - -#define DEFINE_MANAGED_VTABLE(classname) \ -const QV4::ManagedVTable classname::static_vtbl = \ -{ \ - call, \ - construct, \ - markObjects, \ - destroy, \ - 0, \ - hasInstance, \ - get, \ - getIndexed, \ - put, \ - putIndexed, \ - query, \ - queryIndexed, \ - deleteProperty, \ - deleteIndexedProperty, \ - getLookup, \ - setLookup, \ - isEqualTo, \ - advanceIterator, \ - #classname \ -} - -#define DEFINE_MANAGED_VTABLE_WITH_DELETABLES(classname) \ -const QV4::ManagedVTable classname::static_vtbl = \ -{ \ - call, \ - construct, \ - markObjects, \ - destroy, \ - collectDeletables, \ - hasInstance, \ - get, \ - getIndexed, \ - put, \ - putIndexed, \ - query, \ - queryIndexed, \ - deleteProperty, \ - deleteIndexedProperty, \ - getLookup, \ - setLookup, \ - isEqualTo, \ - advanceIterator, \ - #classname \ -} - -struct Q_QML_EXPORT Managed -{ -private: - void *operator new(size_t); - Managed(const Managed &other); - void operator = (const Managed &other); - -protected: - Managed(InternalClass *internal) - : _data(0), vtbl(&static_vtbl), internalClass(internal) - { inUse = 1; extensible = 1; } - -public: - void *operator new(size_t size, MemoryManager *mm); - void operator delete(void *ptr); - void operator delete(void *ptr, MemoryManager *mm); - - inline void mark() { - if (markBit) - return; - markBit = 1; - if (vtbl->markObjects) - vtbl->markObjects(this); - } - - enum Type { - Type_Invalid, - Type_String, - Type_Object, - Type_ArrayObject, - Type_FunctionObject, - Type_BooleanObject, - Type_NumberObject, - Type_StringObject, - Type_DateObject, - Type_RegExpObject, - Type_ErrorObject, - Type_ArgumentsObject, - Type_JSONObject, - Type_MathObject, - Type_ForeachIteratorObject, - Type_RegExp, - - Type_QmlSequence - }; - - ExecutionEngine *engine() const; - - template - T *as() { -#if !defined(QT_NO_QOBJECT_CHECK) - reinterpret_cast(this)->qt_check_for_QMANAGED_macro(*reinterpret_cast(this)); -#endif - return vtbl == &T::static_vtbl ? static_cast(this) : 0; - } - template - const T *as() const { -#if !defined(QT_NO_QOBJECT_CHECK) - reinterpret_cast(this)->qt_check_for_QMANAGED_macro(*reinterpret_cast(const_cast(this))); -#endif - return vtbl == &T::static_vtbl ? static_cast(this) : 0; - } - - ArrayObject *asArrayObject() { return type == Type_ArrayObject ? reinterpret_cast(this) : 0; } - FunctionObject *asFunctionObject() { return type == Type_FunctionObject ? reinterpret_cast(this) : 0; } - BooleanObject *asBooleanObject() { return type == Type_BooleanObject ? reinterpret_cast(this) : 0; } - NumberObject *asNumberObject() { return type == Type_NumberObject ? reinterpret_cast(this) : 0; } - StringObject *asStringObject() { return type == Type_StringObject ? reinterpret_cast(this) : 0; } - DateObject *asDateObject() { return type == Type_DateObject ? reinterpret_cast(this) : 0; } - ErrorObject *asErrorObject() { return type == Type_ErrorObject ? reinterpret_cast(this) : 0; } - ArgumentsObject *asArgumentsObject() { return type == Type_ArgumentsObject ? reinterpret_cast(this) : 0; } - - bool isListType() const { return type == Type_QmlSequence; } - - bool isArrayObject() const { return type == Type_ArrayObject; } - bool isStringObject() const { return type == Type_StringObject; } - - QString className() const; - - Managed **nextFreeRef() { - return reinterpret_cast(this); - } - Managed *nextFree() { - return *reinterpret_cast(this); - } - void setNextFree(Managed *m) { - *reinterpret_cast(this) = m; - } - - inline bool hasInstance(const Value &v) { - return vtbl->hasInstance(this, v); - } - Value construct(Value *args, int argc); - Value call(const Value &thisObject, Value *args, int argc); - Value get(String *name, bool *hasProperty = 0); - Value getIndexed(uint index, bool *hasProperty = 0); - void put(String *name, const Value &value) - { vtbl->put(this, name, value); } - void putIndexed(uint index, const Value &value) - { vtbl->putIndexed(this, index, value); } - PropertyAttributes query(String *name) const - { return vtbl->query(this, name); } - PropertyAttributes queryIndexed(uint index) const - { return vtbl->queryIndexed(this, index); } - - bool deleteProperty(String *name) - { return vtbl->deleteProperty(this, name); } - bool deleteIndexedProperty(uint index) - { return vtbl->deleteIndexedProperty(this, index); } - void getLookup(Lookup *l, Value *result) - { vtbl->getLookup(this, l, result); } - void setLookup(Lookup *l, const Value &v) - { vtbl->setLookup(this, l, v); } - - bool isEqualTo(Managed *other) - { return vtbl->isEqualTo(this, other); } - Property *advanceIterator(ObjectIterator *it, String **name, uint *index, PropertyAttributes *attributes) - { return vtbl->advanceIterator(this, it, name, index, attributes); } - - static void destroy(Managed *that) { that->_data = 0; } - static bool hasInstance(Managed *that, const Value &value); - static Value construct(Managed *m, Value *, int); - static Value call(Managed *m, const Value &, Value *, int); - static void getLookup(Managed *m, Lookup *, Value *); - static void setLookup(Managed *m, Lookup *l, const Value &v); - static bool isEqualTo(Managed *m, Managed *other); - - uint internalType() const { - return type; - } - - union { - uint _data; - struct { - uint markBit : 1; - uint inUse : 1; - uint extensible : 1; // used by Object - uint isNonStrictArgumentsObject : 1; - uint isBuiltinFunction : 1; // used by FunctionObject - uint needsActivation : 1; // used by FunctionObject - uint usesArgumentsObject : 1; // used by FunctionObject - uint strictMode : 1; // used by FunctionObject - uint type : 8; - mutable uint subtype : 3; - uint bindingKeyFlag : 1; - uint unused : 12; - }; - }; - -protected: - - static const ManagedVTable static_vtbl; - - const ManagedVTable *vtbl; -public: - InternalClass *internalClass; - -private: - friend class MemoryManager; - friend struct Identifiers; - friend struct ObjectIterator; -}; - -// ### Not a good placement -template -inline T *Value::as() const { Managed *m = isObject() ? managed() : 0; return m ? m->as() : 0; } - - -} - - -QT_END_NAMESPACE - -#endif diff --git a/src/qml/qml/v4/qv4math_p.h b/src/qml/qml/v4/qv4math_p.h deleted file mode 100644 index a3a3715545..0000000000 --- a/src/qml/qml/v4/qv4math_p.h +++ /dev/null @@ -1,147 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QMLJS_MATH_H -#define QMLJS_MATH_H - -#include - -#ifndef QMLJS_LLVM_RUNTIME -# include -#endif // QMLJS_LLVM_RUNTIME -#include - -#if defined(Q_CC_GNU) -#define QMLJS_READONLY __attribute((const)) -#else -#define QMLJS_READONLY -#endif - -QT_BEGIN_NAMESPACE - -namespace QV4 { - -#if !defined(QMLJS_LLVM_RUNTIME) && defined(Q_CC_GNU) && defined(Q_PROCESSOR_X86) - -static inline QMLJS_READONLY Value add_int32(int a, int b) -{ - quint8 overflow = 0; - int aa = a; - - asm ("addl %2, %1\n" - "seto %0" - : "=q" (overflow), "=r" (aa) - : "r" (b), "1" (aa) - : "cc" - ); - if (!overflow) - return Value::fromInt32(aa); - return Value::fromDouble((double)a + (double)b); -} - -static inline QMLJS_READONLY Value sub_int32(int a, int b) -{ - quint8 overflow = 0; - int aa = a; - - asm ("subl %2, %1\n" - "seto %0" - : "=q" (overflow), "=r" (aa) - : "r" (b), "1" (aa) - : "cc" - ); - if (!overflow) - return Value::fromInt32(aa); - return Value::fromDouble((double)a - (double)b); -} - -static inline QMLJS_READONLY Value mul_int32(int a, int b) -{ - quint8 overflow = 0; - int aa = a; - - asm ("imul %2, %1\n" - "setc %0" - : "=q" (overflow), "=r" (aa) - : "r" (b), "1" (aa) - : "cc" - ); - if (!overflow) - return Value::fromInt32(aa); - return Value::fromDouble((double)a * (double)b); -} - -#else - -static inline QMLJS_READONLY Value add_int32(int a, int b) -{ - qint64 result = a + b; - if (result > INT_MAX || result < INT_MIN) - return Value::fromDouble(result); - return Value::fromInt32(static_cast(result)); -} - -static inline QMLJS_READONLY Value sub_int32(int a, int b) -{ - qint64 result = a - b; - if (result > INT_MAX || result < INT_MIN) - return Value::fromDouble(result); - return Value::fromInt32(static_cast(result)); -} - -static inline QMLJS_READONLY Value mul_int32(int a, int b) -{ - qint64 result = a * b; - if (result > INT_MAX || result < INT_MIN) - return Value::fromDouble(result); - return Value::fromInt32(static_cast(result)); -} - -#endif // defined(QMLJS_INLINE_MATH) - -} - -QT_END_NAMESPACE - -#ifdef QMLJS_READONLY -#undef QMLJS_READONLY -#endif - -#endif // QMLJS_MATH_H diff --git a/src/qml/qml/v4/qv4mathobject.cpp b/src/qml/qml/v4/qv4mathobject.cpp deleted file mode 100644 index 7aa56f51bd..0000000000 --- a/src/qml/qml/v4/qv4mathobject.cpp +++ /dev/null @@ -1,311 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4mathobject_p.h" -#include "qv4objectproto_p.h" - -#include -#include -#include - -using namespace QV4; - -static const double qt_PI = 2.0 * ::asin(1.0); - -MathObject::MathObject(ExecutionContext *ctx) - : Object(ctx->engine) -{ - type = Type_MathObject; - prototype = ctx->engine->objectPrototype; - - defineReadonlyProperty(ctx->engine, QStringLiteral("E"), Value::fromDouble(::exp(1.0))); - defineReadonlyProperty(ctx->engine, QStringLiteral("LN2"), Value::fromDouble(::log(2.0))); - defineReadonlyProperty(ctx->engine, QStringLiteral("LN10"), Value::fromDouble(::log(10.0))); - defineReadonlyProperty(ctx->engine, QStringLiteral("LOG2E"), Value::fromDouble(1.0/::log(2.0))); - defineReadonlyProperty(ctx->engine, QStringLiteral("LOG10E"), Value::fromDouble(1.0/::log(10.0))); - defineReadonlyProperty(ctx->engine, QStringLiteral("PI"), Value::fromDouble(qt_PI)); - defineReadonlyProperty(ctx->engine, QStringLiteral("SQRT1_2"), Value::fromDouble(::sqrt(0.5))); - defineReadonlyProperty(ctx->engine, QStringLiteral("SQRT2"), Value::fromDouble(::sqrt(2.0))); - - defineDefaultProperty(ctx, QStringLiteral("abs"), method_abs, 1); - defineDefaultProperty(ctx, QStringLiteral("acos"), method_acos, 1); - defineDefaultProperty(ctx, QStringLiteral("asin"), method_asin, 0); - defineDefaultProperty(ctx, QStringLiteral("atan"), method_atan, 1); - defineDefaultProperty(ctx, QStringLiteral("atan2"), method_atan2, 2); - defineDefaultProperty(ctx, QStringLiteral("ceil"), method_ceil, 1); - defineDefaultProperty(ctx, QStringLiteral("cos"), method_cos, 1); - defineDefaultProperty(ctx, QStringLiteral("exp"), method_exp, 1); - defineDefaultProperty(ctx, QStringLiteral("floor"), method_floor, 1); - defineDefaultProperty(ctx, QStringLiteral("log"), method_log, 1); - defineDefaultProperty(ctx, QStringLiteral("max"), method_max, 2); - defineDefaultProperty(ctx, QStringLiteral("min"), method_min, 2); - defineDefaultProperty(ctx, QStringLiteral("pow"), method_pow, 2); - defineDefaultProperty(ctx, QStringLiteral("random"), method_random, 0); - defineDefaultProperty(ctx, QStringLiteral("round"), method_round, 1); - defineDefaultProperty(ctx, QStringLiteral("sin"), method_sin, 1); - defineDefaultProperty(ctx, QStringLiteral("sqrt"), method_sqrt, 1); - defineDefaultProperty(ctx, QStringLiteral("tan"), method_tan, 1); -} - -/* copies the sign from y to x and returns the result */ -static double copySign(double x, double y) -{ - uchar *xch = (uchar *)&x; - uchar *ych = (uchar *)&y; - if (QSysInfo::ByteOrder == QSysInfo::BigEndian) - xch[0] = (xch[0] & 0x7f) | (ych[0] & 0x80); - else - xch[7] = (xch[7] & 0x7f) | (ych[7] & 0x80); - return x; -} - -Value MathObject::method_abs(SimpleCallContext *context) -{ - if (!context->argumentCount) - return Value::fromDouble(qSNaN()); - - if (context->arguments[0].isInteger()) { - int i = context->arguments[0].integerValue(); - return Value::fromInt32(i < 0 ? - i : i); - } - - double v = context->arguments[0].toNumber(); - if (v == 0) // 0 | -0 - return Value::fromDouble(0); - - return Value::fromDouble(v < 0 ? -v : v); -} - -Value MathObject::method_acos(SimpleCallContext *context) -{ - double v = context->argumentCount ? context->arguments[0].toNumber() : 2; - if (v > 1) - return Value::fromDouble(qSNaN()); - - return Value::fromDouble(::acos(v)); -} - -Value MathObject::method_asin(SimpleCallContext *context) -{ - double v = context->argumentCount ? context->arguments[0].toNumber() : 2; - if (v > 1) - return Value::fromDouble(qSNaN()); - else - return Value::fromDouble(::asin(v)); -} - -Value MathObject::method_atan(SimpleCallContext *context) -{ - double v = context->argumentCount ? context->arguments[0].toNumber() : qSNaN(); - if (v == 0.0) - return Value::fromDouble(v); - else - return Value::fromDouble(::atan(v)); -} - -Value MathObject::method_atan2(SimpleCallContext *context) -{ - double v1 = context->argumentCount ? context->arguments[0].toNumber() : qSNaN(); - double v2 = context->argumentCount > 1 ? context->arguments[1].toNumber() : qSNaN(); - - if ((v1 < 0) && qIsFinite(v1) && qIsInf(v2) && (copySign(1.0, v2) == 1.0)) - return Value::fromDouble(copySign(0, -1.0)); - - if ((v1 == 0.0) && (v2 == 0.0)) { - if ((copySign(1.0, v1) == 1.0) && (copySign(1.0, v2) == -1.0)) { - return Value::fromDouble(qt_PI); - } else if ((copySign(1.0, v1) == -1.0) && (copySign(1.0, v2) == -1.0)) { - return Value::fromDouble(-qt_PI); - } - } - return Value::fromDouble(::atan2(v1, v2)); -} - -Value MathObject::method_ceil(SimpleCallContext *context) -{ - double v = context->argumentCount ? context->arguments[0].toNumber() : qSNaN(); - if (v < 0.0 && v > -1.0) - return Value::fromDouble(copySign(0, -1.0)); - else - return Value::fromDouble(::ceil(v)); -} - -Value MathObject::method_cos(SimpleCallContext *context) -{ - double v = context->argumentCount ? context->arguments[0].toNumber() : qSNaN(); - return Value::fromDouble(::cos(v)); -} - -Value MathObject::method_exp(SimpleCallContext *context) -{ - double v = context->argumentCount ? context->arguments[0].toNumber() : qSNaN(); - if (qIsInf(v)) { - if (copySign(1.0, v) == -1.0) - return Value::fromDouble(0); - else - return Value::fromDouble(qInf()); - } else { - return Value::fromDouble(::exp(v)); - } -} - -Value MathObject::method_floor(SimpleCallContext *context) -{ - double v = context->argumentCount ? context->arguments[0].toNumber() : qSNaN(); - return Value::fromDouble(::floor(v)); -} - -Value MathObject::method_log(SimpleCallContext *context) -{ - double v = context->argumentCount ? context->arguments[0].toNumber() : qSNaN(); - if (v < 0) - return Value::fromDouble(qSNaN()); - else - return Value::fromDouble(::log(v)); -} - -Value MathObject::method_max(SimpleCallContext *context) -{ - double mx = -qInf(); - for (unsigned i = 0; i < context->argumentCount; ++i) { - double x = context->arguments[i].toNumber(); - if (x > mx || std::isnan(x)) - mx = x; - } - return Value::fromDouble(mx); -} - -Value MathObject::method_min(SimpleCallContext *context) -{ - double mx = qInf(); - for (unsigned i = 0; i < context->argumentCount; ++i) { - double x = context->arguments[i].toNumber(); - if ((x == 0 && mx == x && copySign(1.0, x) == -1.0) - || (x < mx) || std::isnan(x)) { - mx = x; - } - } - return Value::fromDouble(mx); -} - -Value MathObject::method_pow(SimpleCallContext *context) -{ - double x = context->argumentCount > 0 ? context->arguments[0].toNumber() : qSNaN(); - double y = context->argumentCount > 1 ? context->arguments[1].toNumber() : qSNaN(); - - if (std::isnan(y)) - return Value::fromDouble(qSNaN()); - - if (y == 0) { - return Value::fromDouble(1); - } else if (((x == 1) || (x == -1)) && std::isinf(y)) { - return Value::fromDouble(qSNaN()); - } else if (((x == 0) && copySign(1.0, x) == 1.0) && (y < 0)) { - return Value::fromDouble(qInf()); - } else if ((x == 0) && copySign(1.0, x) == -1.0) { - if (y < 0) { - if (::fmod(-y, 2.0) == 1.0) - return Value::fromDouble(-qInf()); - else - return Value::fromDouble(qInf()); - } else if (y > 0) { - if (::fmod(y, 2.0) == 1.0) - return Value::fromDouble(copySign(0, -1.0)); - else - return Value::fromDouble(0); - } - } - -#ifdef Q_OS_AIX - else if (qIsInf(x) && copySign(1.0, x) == -1.0) { - if (y > 0) { - if (::fmod(y, 2.0) == 1.0) - return Value::fromDouble(-qInf()); - else - return Value::fromDouble(qInf()); - } else if (y < 0) { - if (::fmod(-y, 2.0) == 1.0) - return Value::fromDouble(copySign(0, -1.0)); - else - return Value::fromDouble(0); - } - } -#endif - else { - return Value::fromDouble(::pow(x, y)); - } - // ### - return Value::fromDouble(qSNaN()); -} - -Value MathObject::method_random(SimpleCallContext *) -{ - return Value::fromDouble(qrand() / (double) RAND_MAX); -} - -Value MathObject::method_round(SimpleCallContext *context) -{ - double v = context->argumentCount ? context->arguments[0].toNumber() : qSNaN(); - v = copySign(::floor(v + 0.5), v); - return Value::fromDouble(v); -} - -Value MathObject::method_sin(SimpleCallContext *context) -{ - double v = context->argumentCount ? context->arguments[0].toNumber() : qSNaN(); - return Value::fromDouble(::sin(v)); -} - -Value MathObject::method_sqrt(SimpleCallContext *context) -{ - double v = context->argumentCount ? context->arguments[0].toNumber() : qSNaN(); - return Value::fromDouble(::sqrt(v)); -} - -Value MathObject::method_tan(SimpleCallContext *context) -{ - double v = context->argumentCount ? context->arguments[0].toNumber() : qSNaN(); - if (v == 0.0) - return Value::fromDouble(v); - else - return Value::fromDouble(::tan(v)); -} - diff --git a/src/qml/qml/v4/qv4mathobject_p.h b/src/qml/qml/v4/qv4mathobject_p.h deleted file mode 100644 index 03c36bcc68..0000000000 --- a/src/qml/qml/v4/qv4mathobject_p.h +++ /dev/null @@ -1,78 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QV4MATHOBJECT_H -#define QV$MATHOBJECT_H - -#include "qv4object_p.h" - -QT_BEGIN_NAMESPACE - -namespace QV4 { - -struct MathObject: Object -{ - MathObject(ExecutionContext *ctx); - - static Value method_abs(SimpleCallContext *context); - static Value method_acos(SimpleCallContext *context); - static Value method_asin(SimpleCallContext *context); - static Value method_atan(SimpleCallContext *context); - static Value method_atan2(SimpleCallContext *context); - static Value method_ceil(SimpleCallContext *context); - static Value method_cos(SimpleCallContext *context); - static Value method_exp(SimpleCallContext *context); - static Value method_floor(SimpleCallContext *context); - static Value method_log(SimpleCallContext *context); - static Value method_max(SimpleCallContext *context); - static Value method_min(SimpleCallContext *context); - static Value method_pow(SimpleCallContext *context); - static Value method_random(SimpleCallContext *context); - static Value method_round(SimpleCallContext *context); - static Value method_sin(SimpleCallContext *context); - static Value method_sqrt(SimpleCallContext *context); - static Value method_tan(SimpleCallContext *context); -}; - -} - -QT_END_NAMESPACE - -#endif // QMLJS_OBJECTS_H diff --git a/src/qml/qml/v4/qv4mm.cpp b/src/qml/qml/v4/qv4mm.cpp deleted file mode 100644 index 0e53a2088f..0000000000 --- a/src/qml/qml/v4/qv4mm.cpp +++ /dev/null @@ -1,605 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4engine_p.h" -#include "qv4object_p.h" -#include "qv4objectproto_p.h" -#include "qv4mm_p.h" -#include "qv4qobjectwrapper_p.h" -#include -#include "PageAllocation.h" -#include "StdLibExtras.h" - -#include -#include -#include -#include - -#include -#include -#include "qv4alloca_p.h" - -#ifdef V4_USE_VALGRIND -#include -#include -#endif - -#if OS(QNX) -#include // __tls() -#endif - -QT_BEGIN_NAMESPACE - -using namespace QV4; -using namespace WTF; - -static const std::size_t CHUNK_SIZE = 1024*32; - -struct MemoryManager::Data -{ - bool enableGC; - bool gcBlocked; - bool scribble; - bool aggressiveGC; - ExecutionEngine *engine; - quintptr *stackTop; - - enum { MaxItemSize = 512 }; - Managed *smallItems[MaxItemSize/16]; - uint nChunks[MaxItemSize/16]; - uint availableItems[MaxItemSize/16]; - uint allocCount[MaxItemSize/16]; - struct Chunk { - PageAllocation memory; - int chunkSize; - }; - - QVector heapChunks; - QHash protectedObject; - - // statistics: -#ifdef DETAILED_MM_STATS - QVector allocSizeCounters; -#endif // DETAILED_MM_STATS - - Data(bool enableGC) - : enableGC(enableGC) - , gcBlocked(false) - , engine(0) - , stackTop(0) - { - memset(smallItems, 0, sizeof(smallItems)); - memset(nChunks, 0, sizeof(nChunks)); - memset(availableItems, 0, sizeof(availableItems)); - memset(allocCount, 0, sizeof(allocCount)); - scribble = !qgetenv("MM_SCRIBBLE").isEmpty(); - aggressiveGC = !qgetenv("MM_AGGRESSIVE_GC").isEmpty(); - } - - ~Data() - { - for (QVector::iterator i = heapChunks.begin(), ei = heapChunks.end(); i != ei; ++i) - i->memory.deallocate(); - } -}; - -#define SCRIBBLE(obj, c, size) \ - if (m_d->scribble) \ - ::memset((void *)(obj + 1), c, size - sizeof(Managed)); - - -namespace QV4 { - -bool operator<(const MemoryManager::Data::Chunk &a, const MemoryManager::Data::Chunk &b) -{ - return a.memory.base() < b.memory.base(); -} - -} // namespace QV4 - -MemoryManager::MemoryManager() - : m_d(new Data(true)) - , m_contextList(0) - , m_persistentValues(0) - , m_weakValues(0) -{ - setEnableGC(true); -#ifdef V4_USE_VALGRIND - VALGRIND_CREATE_MEMPOOL(this, 0, true); -#endif - -#if OS(QNX) - // TLS is at the top of each thread's stack, - // so the stack base for thread is the result of __tls() - m_d->stackTop = reinterpret_cast( - (((uintptr_t)__tls() + __PAGESIZE - 1) & ~(__PAGESIZE - 1))); -#elif USE(PTHREADS) -# if OS(DARWIN) - void *st = pthread_get_stackaddr_np(pthread_self()); - m_d->stackTop = static_cast(st); -# else - void* stackBottom = 0; - pthread_attr_t attr; - pthread_getattr_np(pthread_self(), &attr); - size_t stackSize = 0; - pthread_attr_getstack(&attr, &stackBottom, &stackSize); - pthread_attr_destroy(&attr); - - m_d->stackTop = static_cast(stackBottom) + stackSize/sizeof(quintptr); -# endif -#elif OS(WINDOWS) - PNT_TIB tib = (PNT_TIB)NtCurrentTeb(); - m_d->stackTop = static_cast(tib->StackBase); -#else -# error "Unsupported platform: no way to get the top-of-stack." -#endif - -} - -Managed *MemoryManager::alloc(std::size_t size) -{ - if (m_d->aggressiveGC) - runGC(); -#ifdef DETAILED_MM_STATS - willAllocate(size); -#endif // DETAILED_MM_STATS - - assert(size >= 16); - assert(size % 16 == 0); - - size_t pos = size >> 4; - ++m_d->allocCount[pos]; - - // fits into a small bucket - assert(size < MemoryManager::Data::MaxItemSize); - - Managed *m = m_d->smallItems[pos]; - if (m) - goto found; - - // try to free up space, otherwise allocate - if (m_d->allocCount[pos] > (m_d->availableItems[pos] >> 1) && !m_d->aggressiveGC) { - runGC(); - m = m_d->smallItems[pos]; - if (m) - goto found; - } - - // no free item available, allocate a new chunk - { - // allocate larger chunks at a time to avoid excessive GC, but cap at 64M chunks - uint shift = ++m_d->nChunks[pos]; - if (shift > 10) - shift = 10; - std::size_t allocSize = CHUNK_SIZE*(1 << shift); - allocSize = roundUpToMultipleOf(WTF::pageSize(), allocSize); - Data::Chunk allocation; - allocation.memory = PageAllocation::allocate(allocSize, OSAllocator::JSGCHeapPages); - allocation.chunkSize = size; - m_d->heapChunks.append(allocation); - qSort(m_d->heapChunks); - char *chunk = (char *)allocation.memory.base(); - char *end = chunk + allocation.memory.size() - size; - memset(chunk, 0, allocation.memory.size()); - Managed **last = &m_d->smallItems[pos]; - while (chunk <= end) { - Managed *o = reinterpret_cast(chunk); - o->_data = 0; - *last = o; - last = o->nextFreeRef(); - chunk += size; - } - *last = 0; - m = m_d->smallItems[pos]; - m_d->availableItems[pos] += allocation.memory.size()/size - 1; -#ifdef V4_USE_VALGRIND - VALGRIND_MAKE_MEM_NOACCESS(allocation.memory, allocation.chunkSize); -#endif - } - - found: -#ifdef V4_USE_VALGRIND - VALGRIND_MEMPOOL_ALLOC(this, m, size); -#endif - - m_d->smallItems[pos] = m->nextFree(); - return m; -} - -void MemoryManager::mark() -{ - m_d->engine->markObjects(); - - for (QHash::const_iterator it = m_d->protectedObject.begin(); it != m_d->protectedObject.constEnd(); ++it) - it.key()->mark(); - - PersistentValuePrivate *persistent = m_persistentValues; - while (persistent) { - if (!persistent->refcount) { - PersistentValuePrivate *n = persistent->next; - persistent->removeFromList(); - delete persistent; - persistent = n; - continue; - } - if (Managed *m = persistent->value.asManaged()) - m->mark(); - persistent = persistent->next; - } - - // push all caller saved registers to the stack, so we can find the objects living in these registers -#if COMPILER(MSVC) -# if CPU(X86_64) - HANDLE thread = GetCurrentThread(); - WOW64_CONTEXT ctxt; - /*bool success =*/ Wow64GetThreadContext(thread, &ctxt); -# elif CPU(X86) - HANDLE thread = GetCurrentThread(); - CONTEXT ctxt; - /*bool success =*/ GetThreadContext(thread, &ctxt); -# endif // CPU -#elif COMPILER(CLANG) || COMPILER(GCC) -# if CPU(X86_64) - quintptr regs[5]; - asm( - "mov %%rbp, %0\n" - "mov %%r12, %1\n" - "mov %%r13, %2\n" - "mov %%r14, %3\n" - "mov %%r15, %4\n" - : "=m" (regs[0]), "=m" (regs[1]), "=m" (regs[2]), "=m" (regs[3]), "=m" (regs[4]) - : - : - ); -# endif // CPU -#endif // COMPILER - - collectFromStack(); - - // Preserve QObject ownership rules within JavaScript: A parent with c++ ownership - // keeps all of its children alive in JavaScript. - - // Do this _after_ collectFromStack to ensure that processing the weak - // managed objects in the loop down there doesn't make then end up as leftovers - // on the stack and thus always get collected. - for (PersistentValuePrivate *weak = m_weakValues; weak; weak = weak->next) { - if (!weak->refcount) - continue; - QObjectWrapper *qobjectWrapper = weak->value.as(); - if (!qobjectWrapper) - continue; - QObject *qobject = qobjectWrapper->object(); - if (!qobject) - continue; - bool keepAlive = QQmlData::keepAliveDuringGarbageCollection(qobject); - - if (!keepAlive) { - if (QObject *parent = qobject->parent()) { - while (parent->parent()) - parent = parent->parent(); - - keepAlive = QQmlData::keepAliveDuringGarbageCollection(parent); - } - } - - if (keepAlive) - qobjectWrapper->mark(); - } -} - -std::size_t MemoryManager::sweep(bool lastSweep) -{ - PersistentValuePrivate *weak = m_weakValues; - while (weak) { - if (!weak->refcount) { - PersistentValuePrivate *n = weak->next; - weak->removeFromList(); - delete weak; - weak = n; - continue; - } - if (Managed *m = weak->value.asManaged()) { - if (!m->markBit) { - weak->value = Value::emptyValue(); - PersistentValuePrivate *n = weak->next; - weak->removeFromList(); - weak = n; - continue; - } - } - weak = weak->next; - } - - if (MultiplyWrappedQObjectMap *multiplyWrappedQObjects = m_d->engine->m_multiplyWrappedQObjects) { - for (MultiplyWrappedQObjectMap::Iterator it = multiplyWrappedQObjects->begin(); it != multiplyWrappedQObjects->end();) { - if (!it.value()->markBit) - it = multiplyWrappedQObjects->erase(it); - else - ++it; - } - } - - std::size_t freedCount = 0; - GCDeletable *deletable = 0; - GCDeletable **firstDeletable = &deletable; - - for (QVector::iterator i = m_d->heapChunks.begin(), ei = m_d->heapChunks.end(); i != ei; ++i) - freedCount += sweep(reinterpret_cast(i->memory.base()), i->memory.size(), i->chunkSize, &deletable); - - ExecutionContext *ctx = m_contextList; - ExecutionContext **n = &m_contextList; - while (ctx) { - ExecutionContext *next = ctx->next; - if (!ctx->marked) { - free(ctx); - *n = next; - } else { - ctx->marked = false; - n = &ctx->next; - } - ctx = next; - } - - deletable = *firstDeletable; - while (deletable) { - GCDeletable *next = deletable->next; - deletable->lastCall = lastSweep; - delete deletable; - deletable = next; - } - - return freedCount; -} - -std::size_t MemoryManager::sweep(char *chunkStart, std::size_t chunkSize, size_t size, GCDeletable **deletable) -{ -// qDebug("chunkStart @ %p, size=%x, pos=%x (%x)", chunkStart, size, size>>4, m_d->smallItems[size >> 4]); - std::size_t freedCount = 0; - - Managed **f = &m_d->smallItems[size >> 4]; - -#ifdef V4_USE_VALGRIND - VALGRIND_DISABLE_ERROR_REPORTING; -#endif - for (char *chunk = chunkStart, *chunkEnd = chunk + chunkSize - size; chunk <= chunkEnd; chunk += size) { - Managed *m = reinterpret_cast(chunk); -// qDebug("chunk @ %p, size = %lu, in use: %s, mark bit: %s", -// chunk, m->size, (m->inUse ? "yes" : "no"), (m->markBit ? "true" : "false")); - - assert((intptr_t) chunk % 16 == 0); - - if (m->inUse) { - if (m->markBit) { - m->markBit = 0; - } else { -// qDebug() << "-- collecting it." << m << *f << m->nextFree(); -#ifdef V4_USE_VALGRIND - VALGRIND_ENABLE_ERROR_REPORTING; -#endif - if (m->vtbl->collectDeletables) - m->vtbl->collectDeletables(m, deletable); - m->vtbl->destroy(m); - - m->setNextFree(*f); -#ifdef V4_USE_VALGRIND - VALGRIND_DISABLE_ERROR_REPORTING; - VALGRIND_MEMPOOL_FREE(this, m); -#endif - *f = m; - SCRIBBLE(m, 0x99, size); - ++freedCount; - } - } - } -#ifdef V4_USE_VALGRIND - VALGRIND_ENABLE_ERROR_REPORTING; -#endif - - return freedCount; -} - -bool MemoryManager::isGCBlocked() const -{ - return m_d->gcBlocked; -} - -void MemoryManager::setGCBlocked(bool blockGC) -{ - m_d->gcBlocked = blockGC; -} - -void MemoryManager::runGC() -{ - if (!m_d->enableGC || m_d->gcBlocked) { -// qDebug() << "Not running GC."; - return; - } - -// QTime t; t.start(); - -// qDebug() << ">>>>>>>>runGC"; - - mark(); -// std::cerr << "GC: marked " << marks -// << " objects in " << t.elapsed() -// << "ms" << std::endl; - -// t.restart(); - /*std::size_t freedCount =*/ sweep(); -// std::cerr << "GC: sweep freed " << freedCount -// << " objects in " << t.elapsed() -// << "ms" << std::endl; - memset(m_d->allocCount, 0, sizeof(m_d->allocCount)); -} - -void MemoryManager::setEnableGC(bool enableGC) -{ - m_d->enableGC = enableGC; -} - -MemoryManager::~MemoryManager() -{ - PersistentValuePrivate *persistent = m_persistentValues; - while (persistent) { - PersistentValuePrivate *n = persistent->next; - persistent->value = Value::undefinedValue(); - persistent->engine = 0; - persistent->prev = 0; - persistent->next = 0; - persistent = n; - } - - sweep(/*lastSweep*/true); -#ifdef V4_USE_VALGRIND - VALGRIND_DESTROY_MEMPOOL(this); -#endif -} - -void MemoryManager::protect(Managed *m) -{ - ++m_d->protectedObject[m]; -} - -void MemoryManager::unprotect(Managed *m) -{ - if (!--m_d->protectedObject[m]) - m_d->protectedObject.remove(m); -} - -static inline void add(QVector &values, const Value &v) -{ - if (Object *o = v.asObject()) - values.append(o); -} - -void MemoryManager::setExecutionEngine(ExecutionEngine *engine) -{ - m_d->engine = engine; -} - -void MemoryManager::dumpStats() const -{ -#ifdef DETAILED_MM_STATS - std::cerr << "=================" << std::endl; - std::cerr << "Allocation stats:" << std::endl; - std::cerr << "Requests for each chunk size:" << std::endl; - for (int i = 0; i < m_d->allocSizeCounters.size(); ++i) { - if (unsigned count = m_d->allocSizeCounters[i]) { - std::cerr << "\t" << (i << 4) << " bytes chunks: " << count << std::endl; - } - } -#endif // DETAILED_MM_STATS -} - -ExecutionEngine *MemoryManager::engine() const -{ - return m_d->engine; -} - -#ifdef DETAILED_MM_STATS -void MemoryManager::willAllocate(std::size_t size) -{ - unsigned alignedSize = (size + 15) >> 4; - QVector &counters = m_d->allocSizeCounters; - if ((unsigned) counters.size() < alignedSize + 1) - counters.resize(alignedSize + 1); - counters[alignedSize]++; -} - -#endif // DETAILED_MM_STATS - -void MemoryManager::collectFromStack() const -{ - quintptr valueOnStack = 0; - - if (!m_d->heapChunks.count()) - return; - - quintptr *current = (&valueOnStack) + 1; -// qDebug() << "collectFromStack";// << top << current << &valueOnStack; - -#if V4_USE_VALGRIND - VALGRIND_MAKE_MEM_DEFINED(current, (m_d->stackTop - current)*sizeof(quintptr)); -#endif - - char** heapChunkBoundaries = (char**)alloca(m_d->heapChunks.count() * 2 * sizeof(char*)); - char** heapChunkBoundariesEnd = heapChunkBoundaries + 2 * m_d->heapChunks.count(); - int i = 0; - for (QVector::Iterator it = m_d->heapChunks.begin(), end = - m_d->heapChunks.end(); it != end; ++it) { - heapChunkBoundaries[i++] = reinterpret_cast(it->memory.base()) - 1; - heapChunkBoundaries[i++] = reinterpret_cast(it->memory.base()) + it->memory.size() - it->chunkSize; - } - assert(i == m_d->heapChunks.count() * 2); - - for (; current < m_d->stackTop; ++current) { - char* genericPtr = -#if QT_POINTER_SIZE == 8 - reinterpret_cast((*current) & ~(quint64(Value::Type_Mask) << Value::Tag_Shift)); -#else - reinterpret_cast(*current); -#endif - - if (genericPtr < *heapChunkBoundaries || genericPtr > *(heapChunkBoundariesEnd - 1)) - continue; - int index = qLowerBound(heapChunkBoundaries, heapChunkBoundariesEnd, genericPtr) - heapChunkBoundaries; - // An odd index means the pointer is _before_ the end of a heap chunk and therefore valid. - assert(index >= 0 && index < m_d->heapChunks.count() * 2); - if (index & 1) { - int size = m_d->heapChunks.at(index >> 1).chunkSize; - Managed *m = reinterpret_cast(genericPtr); -// qDebug() << " inside" << size; - - if (((quintptr)m - (quintptr)heapChunkBoundaries[index-1] - 1 ) % size) - // wrongly aligned value, skip it - continue; - - if (!m->inUse) - // Skip pointers to already freed objects, they are bogus as well - continue; - -// qDebug() << " marking"; - m->mark(); - } - } -} - -QT_END_NAMESPACE diff --git a/src/qml/qml/v4/qv4mm_p.h b/src/qml/qml/v4/qv4mm_p.h deleted file mode 100644 index f72d23dc9a..0000000000 --- a/src/qml/qml/v4/qv4mm_p.h +++ /dev/null @@ -1,157 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QV4GC_H -#define QV4GC_H - -#include "qv4global_p.h" -#include "qv4context_p.h" - -#include - -//#define DETAILED_MM_STATS - -QT_BEGIN_NAMESPACE - -namespace QV4 { - -struct ExecutionEngine; -struct ExecutionContext; -struct Managed; -struct GCDeletable; - -class Q_QML_EXPORT MemoryManager -{ - MemoryManager(const MemoryManager &); - MemoryManager &operator=(const MemoryManager&); - -public: - struct Data; - - class GCBlocker - { - public: - GCBlocker(MemoryManager *mm) - : mm(mm) - , wasBlocked(mm->isGCBlocked()) - { - mm->setGCBlocked(true); - } - - ~GCBlocker() - { - mm->setGCBlocked(wasBlocked); - } - - private: - MemoryManager *mm; - bool wasBlocked; - }; - -public: - MemoryManager(); - ~MemoryManager(); - - void protect(Managed *m); - void unprotect(Managed *m); - - // TODO: this is only for 64bit (and x86 with SSE/AVX), so exend it for other architectures to be slightly more efficient (meaning, align on 8-byte boundaries). - // Note: all occurances of "16" in alloc/dealloc are also due to the alignment. - static inline std::size_t align(std::size_t size) - { return (size + 15) & ~0xf; } - - inline Managed *allocManaged(std::size_t size) - { - size = align(size); - Managed *o = alloc(size); - return o; - } - - ExecutionContext *allocContext(uint size); - - bool isGCBlocked() const; - void setGCBlocked(bool blockGC); - void runGC(); - - void setEnableGC(bool enableGC); - void setExecutionEngine(ExecutionEngine *engine); - - void dumpStats() const; - -protected: - /// expects size to be aligned - // TODO: try to inline - Managed *alloc(std::size_t size); - - ExecutionEngine *engine() const; - -#ifdef DETAILED_MM_STATS - void willAllocate(std::size_t size); -#endif // DETAILED_MM_STATS - -private: - void collectFromStack() const; - void mark(); - std::size_t sweep(bool lastSweep = false); - std::size_t sweep(char *chunkStart, std::size_t chunkSize, size_t size, GCDeletable **deletable); - -protected: - QScopedPointer m_d; - ExecutionContext *m_contextList; -public: - PersistentValuePrivate *m_persistentValues; - PersistentValuePrivate *m_weakValues; -}; - -inline ExecutionContext *MemoryManager::allocContext(uint size) -{ - ExecutionContext *newContext = (ExecutionContext *)malloc(size); - newContext->next = m_contextList; - m_contextList = newContext; - return newContext; -} - - -} - -QT_END_NAMESPACE - -#endif // QV4GC_H diff --git a/src/qml/qml/v4/qv4numberobject.cpp b/src/qml/qml/v4/qv4numberobject.cpp deleted file mode 100644 index 266fa792dc..0000000000 --- a/src/qml/qml/v4/qv4numberobject.cpp +++ /dev/null @@ -1,257 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4numberobject_p.h" -#include -#include -#include -#include -#include - -using namespace QV4; - -DEFINE_MANAGED_VTABLE(NumberCtor); - -NumberCtor::NumberCtor(ExecutionContext *scope) - : FunctionObject(scope, scope->engine->newIdentifier(QStringLiteral("Number"))) -{ - vtbl = &static_vtbl; -} - -Value NumberCtor::construct(Managed *m, Value *args, int argc) -{ - double d = argc ? args[0].toNumber() : 0.; - return Value::fromObject(m->engine()->newNumberObject(Value::fromDouble(d))); -} - -Value NumberCtor::call(Managed *, const Value &, Value *argv, int argc) -{ - double d = argc ? argv[0].toNumber() : 0.; - return Value::fromDouble(d); -} - -void NumberPrototype::init(ExecutionContext *ctx, const Value &ctor) -{ - ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); - ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); - - ctor.objectValue()->defineReadonlyProperty(ctx->engine, QStringLiteral("NaN"), Value::fromDouble(qSNaN())); - ctor.objectValue()->defineReadonlyProperty(ctx->engine, QStringLiteral("NEGATIVE_INFINITY"), Value::fromDouble(-qInf())); - ctor.objectValue()->defineReadonlyProperty(ctx->engine, QStringLiteral("POSITIVE_INFINITY"), Value::fromDouble(qInf())); - ctor.objectValue()->defineReadonlyProperty(ctx->engine, QStringLiteral("MAX_VALUE"), Value::fromDouble(1.7976931348623158e+308)); - -#ifdef __INTEL_COMPILER -# pragma warning( push ) -# pragma warning(disable: 239) -#endif - ctor.objectValue()->defineReadonlyProperty(ctx->engine, QStringLiteral("MIN_VALUE"), Value::fromDouble(5e-324)); -#ifdef __INTEL_COMPILER -# pragma warning( pop ) -#endif - - defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); - defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString); - defineDefaultProperty(ctx, QStringLiteral("toLocaleString"), method_toLocaleString); - defineDefaultProperty(ctx, QStringLiteral("valueOf"), method_valueOf); - defineDefaultProperty(ctx, QStringLiteral("toFixed"), method_toFixed, 1); - defineDefaultProperty(ctx, QStringLiteral("toExponential"), method_toExponential); - defineDefaultProperty(ctx, QStringLiteral("toPrecision"), method_toPrecision); -} - -Value NumberPrototype::method_toString(SimpleCallContext *ctx) -{ - double num; - if (ctx->thisObject.isNumber()) { - num = ctx->thisObject.asDouble(); - } else { - NumberObject *thisObject = ctx->thisObject.asNumberObject(); - if (!thisObject) - ctx->throwTypeError(); - num = thisObject->value.asDouble(); - } - - Value arg = ctx->argument(0); - if (!arg.isUndefined()) { - int radix = arg.toInt32(); - if (radix < 2 || radix > 36) { - ctx->throwError(QString::fromLatin1("Number.prototype.toString: %0 is not a valid radix") - .arg(radix)); - return Value::undefinedValue(); - } - - if (std::isnan(num)) { - return Value::fromString(ctx, QStringLiteral("NaN")); - } else if (qIsInf(num)) { - return Value::fromString(ctx, QLatin1String(num < 0 ? "-Infinity" : "Infinity")); - } - - if (radix != 10) { - QString str; - bool negative = false; - if (num < 0) { - negative = true; - num = -num; - } - double frac = num - ::floor(num); - num = Value::toInteger(num); - do { - char c = (char)::fmod(num, radix); - c = (c < 10) ? (c + '0') : (c - 10 + 'a'); - str.prepend(QLatin1Char(c)); - num = ::floor(num / radix); - } while (num != 0); - if (frac != 0) { - str.append(QLatin1Char('.')); - do { - frac = frac * radix; - char c = (char)::floor(frac); - c = (c < 10) ? (c + '0') : (c - 10 + 'a'); - str.append(QLatin1Char(c)); - frac = frac - ::floor(frac); - } while (frac != 0); - } - if (negative) - str.prepend(QLatin1Char('-')); - return Value::fromString(ctx, str); - } - } - - String *str = Value::fromDouble(num).toString(ctx); - return Value::fromString(str); -} - -Value NumberPrototype::method_toLocaleString(SimpleCallContext *ctx) -{ - NumberObject *thisObject = ctx->thisObject.asNumberObject(); - if (!thisObject) - ctx->throwTypeError(); - - String *str = thisObject->value.toString(ctx); - return Value::fromString(str); -} - -Value NumberPrototype::method_valueOf(SimpleCallContext *ctx) -{ - NumberObject *thisObject = ctx->thisObject.asNumberObject(); - if (!thisObject) - ctx->throwTypeError(); - - return thisObject->value; -} - -Value NumberPrototype::method_toFixed(SimpleCallContext *ctx) -{ - NumberObject *thisObject = ctx->thisObject.asNumberObject(); - if (!thisObject) - ctx->throwTypeError(); - - double fdigits = 0; - - if (ctx->argumentCount > 0) - fdigits = ctx->argument(0).toInteger(); - - if (std::isnan(fdigits)) - fdigits = 0; - - if (fdigits < 0 || fdigits > 20) - ctx->throwRangeError(ctx->thisObject); - - double v = thisObject->value.asDouble(); - QString str; - if (std::isnan(v)) - str = QString::fromLatin1("NaN"); - else if (qIsInf(v)) - str = QString::fromLatin1(v < 0 ? "-Infinity" : "Infinity"); - else if (v < 1.e21) - str = QString::number(v, 'f', int (fdigits)); - else - return __qmljs_string_from_number(ctx, v); - return Value::fromString(ctx, str); -} - -Value NumberPrototype::method_toExponential(SimpleCallContext *ctx) -{ - NumberObject *thisObject = ctx->thisObject.asNumberObject(); - if (!thisObject) - ctx->throwTypeError(); - - Value fraction = ctx->argument(0); - int fdigits = -1; - - if (!fraction.isUndefined()) { - int fdigits = ctx->argument(0).toInt32(); - if (fdigits < 0 || fdigits > 20) { - String *error = ctx->engine->newString(QStringLiteral("Number.prototype.toExponential: fractionDigits out of range")); - ctx->throwRangeError(Value::fromString(error)); - } - } - - char str[100]; - double_conversion::StringBuilder builder(str, sizeof(str)); - double_conversion::DoubleToStringConverter::EcmaScriptConverter().ToExponential(thisObject->value.asDouble(), fdigits, &builder); - QString result = QString::fromLatin1(builder.Finalize()); - - return Value::fromString(ctx, result); -} - -Value NumberPrototype::method_toPrecision(SimpleCallContext *ctx) -{ - NumberObject *thisObject = ctx->thisObject.asNumberObject(); - if (!thisObject) - ctx->throwTypeError(); - - Value prec = ctx->argument(0); - if (prec.isUndefined()) - return __qmljs_to_string(thisObject->value, ctx); - - double precision = prec.toInt32(); - if (precision < 1 || precision > 21) { - String *error = ctx->engine->newString(QStringLiteral("Number.prototype.toPrecision: precision out of range")); - ctx->throwRangeError(Value::fromString(error)); - } - - char str[100]; - double_conversion::StringBuilder builder(str, sizeof(str)); - double_conversion::DoubleToStringConverter::EcmaScriptConverter().ToPrecision(thisObject->value.asDouble(), precision, &builder); - QString result = QString::fromLatin1(builder.Finalize()); - - return Value::fromString(ctx, result); -} diff --git a/src/qml/qml/v4/qv4numberobject_p.h b/src/qml/qml/v4/qv4numberobject_p.h deleted file mode 100644 index 0c06451c98..0000000000 --- a/src/qml/qml/v4/qv4numberobject_p.h +++ /dev/null @@ -1,81 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QV4NUMBEROBJECT_H -#define QV4NUMBEROBJECT_H - -#include "qv4object_p.h" -#include "qv4functionobject_p.h" -#include - -QT_BEGIN_NAMESPACE - -namespace QV4 { - -struct NumberCtor: FunctionObject -{ - NumberCtor(ExecutionContext *scope); - - static Value construct(Managed *that, Value *args, int argc); - static Value call(Managed *, const Value &, Value *, int); - -protected: - static const ManagedVTable static_vtbl; -}; - -struct NumberPrototype: NumberObject -{ - NumberPrototype(ExecutionEngine *engine): NumberObject(engine, Value::fromDouble(0)) {} - void init(ExecutionContext *ctx, const Value &ctor); - - static Value method_toString(SimpleCallContext *ctx); - static Value method_toLocaleString(SimpleCallContext *ctx); - static Value method_valueOf(SimpleCallContext *ctx); - static Value method_toFixed(SimpleCallContext *ctx); - static Value method_toExponential(SimpleCallContext *ctx); - static Value method_toPrecision(SimpleCallContext *ctx); -}; - - -} - -QT_END_NAMESPACE - -#endif // QV4ECMAOBJECTS_P_H diff --git a/src/qml/qml/v4/qv4object.cpp b/src/qml/qml/v4/qv4object.cpp deleted file mode 100644 index edfb535e7a..0000000000 --- a/src/qml/qml/v4/qv4object.cpp +++ /dev/null @@ -1,1407 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4object_p.h" -#include "qv4jsir_p.h" -#include "qv4isel_p.h" -#include "qv4objectproto_p.h" -#include "qv4stringobject_p.h" -#include "qv4argumentsobject_p.h" -#include "qv4mm_p.h" -#include "qv4lookup_p.h" - -#include -#include -#include -#include -#include -#include -#include "private/qlocale_tools_p.h" - -#include -#include -#include -#include -#include -#include -#include "qv4alloca_p.h" - -using namespace QV4; - -DEFINE_MANAGED_VTABLE(Object); - -Object::Object(ExecutionEngine *engine) - : Managed(engine->emptyClass) - , prototype(0) - , memberDataAlloc(InlinePropertySize), memberData(inlineProperties) - , arrayOffset(0), arrayDataLen(0), arrayAlloc(0), arrayAttributes(0), arrayData(0), sparseArray(0) -{ - vtbl = &static_vtbl; - type = Type_Object; - memset(memberData, 0, sizeof(Property)*memberDataAlloc); -} - -Object::Object(ExecutionContext *context) - : Managed(context->engine->emptyClass) - , prototype(0) - , memberDataAlloc(InlinePropertySize), memberData(inlineProperties) - , arrayOffset(0), arrayDataLen(0), arrayAlloc(0), arrayAttributes(0), arrayData(0), sparseArray(0) -{ - vtbl = &static_vtbl; - type = Type_Object; - memset(memberData, 0, sizeof(Property)*memberDataAlloc); -} - -Object::Object(ExecutionEngine *engine, InternalClass *internalClass) - : Managed(internalClass) - , prototype(0) - , memberDataAlloc(InlinePropertySize), memberData(inlineProperties) - , arrayOffset(0), arrayDataLen(0), arrayAlloc(0), arrayAttributes(0), arrayData(0), sparseArray(0) -{ - vtbl = &static_vtbl; - type = Type_Object; - - if (internalClass->size >= memberDataAlloc) { - memberDataAlloc = internalClass->size; - memberData = new Property[memberDataAlloc]; - } - memset(memberData, 0, sizeof(Property)*memberDataAlloc); -} - -Object::~Object() -{ - if (memberData != inlineProperties) - delete [] memberData; - delete [] (arrayData - (sparseArray ? 0 : arrayOffset)); - if (arrayAttributes) - delete [] (arrayAttributes - (sparseArray ? 0 : arrayOffset)); - delete sparseArray; - _data = 0; -} - -void Object::destroy(Managed *that) -{ - static_cast(that)->~Object(); -} - -void Object::put(ExecutionContext *ctx, const QString &name, const Value &value) -{ - put(ctx->engine->newString(name), value); -} - -Value Object::getValue(const Value &thisObject, const Property *p, PropertyAttributes attrs) -{ - if (!attrs.isAccessor()) - return p->value; - FunctionObject *getter = p->getter(); - if (!getter) - return Value::undefinedValue(); - - return getter->call(thisObject, 0, 0); -} - -void Object::putValue(Property *pd, PropertyAttributes attrs, const Value &value) -{ - if (attrs.isAccessor()) { - if (pd->set) { - Value args[1]; - args[0] = value; - pd->set->call(Value::fromObject(this), args, 1); - return; - } - goto reject; - } - - if (!attrs.isWritable()) - goto reject; - - pd->value = value; - return; - - reject: - if (engine()->current->strictMode) - engine()->current->throwTypeError(); - -} - -void Object::inplaceBinOp(ExecutionContext *, BinOp op, String *name, const Value &rhs) -{ - Value v = get(name); - Value result; - op(&result, v, rhs); - put(name, result); -} - -void Object::inplaceBinOp(ExecutionContext *ctx, BinOp op, const Value &index, const Value &rhs) -{ - uint idx = index.asArrayIndex(); - if (idx < UINT_MAX) { - bool hasProperty = false; - Value v = getIndexed(idx, &hasProperty); - Value result; - op(&result, v, rhs); - putIndexed(idx, result); - return; - } - String *name = index.toString(ctx); - assert(name); - inplaceBinOp(ctx, op, name, rhs); -} - -void Object::inplaceBinOp(ExecutionContext *ctx, BinOpContext op, String *name, const Value &rhs) -{ - Value v = get(name); - Value result; - op(ctx, &result, v, rhs); - put(name, result); -} - -void Object::inplaceBinOp(ExecutionContext *ctx, BinOpContext op, const Value &index, const Value &rhs) -{ - uint idx = index.asArrayIndex(); - if (idx < UINT_MAX) { - bool hasProperty = false; - Value v = getIndexed(idx, &hasProperty); - Value result; - op(ctx, &result, v, rhs); - putIndexed(idx, result); - return; - } - String *name = index.toString(ctx); - assert(name); - inplaceBinOp(ctx, op, name, rhs); -} - -void Object::defineDefaultProperty(String *name, Value value) -{ - Property *pd = insertMember(name, Attr_Data|Attr_NotEnumerable); - pd->value = value; -} - -void Object::defineDefaultProperty(ExecutionContext *context, const QString &name, Value value) -{ - defineDefaultProperty(context->engine->newIdentifier(name), value); -} - -void Object::defineDefaultProperty(ExecutionEngine *engine, const QString &name, Value value) -{ - defineDefaultProperty(engine->newIdentifier(name), value); -} - -void Object::defineDefaultProperty(ExecutionContext *context, const QString &name, Value (*code)(SimpleCallContext *), int argumentCount) -{ - Q_UNUSED(argumentCount); - String *s = context->engine->newIdentifier(name); - FunctionObject* function = context->engine->newBuiltinFunction(context, s, code); - function->defineReadonlyProperty(context->engine->id_length, Value::fromInt32(argumentCount)); - defineDefaultProperty(s, Value::fromObject(function)); -} - -void Object::defineDefaultProperty(ExecutionEngine *engine, const QString &name, Value (*code)(SimpleCallContext *), int argumentCount) -{ - Q_UNUSED(argumentCount); - String *s = engine->newIdentifier(name); - FunctionObject* function = engine->newBuiltinFunction(engine->rootContext, s, code); - function->defineReadonlyProperty(engine->id_length, Value::fromInt32(argumentCount)); - defineDefaultProperty(s, Value::fromObject(function)); -} - -void Object::defineAccessorProperty(ExecutionEngine *engine, const QString &name, - Value (*getter)(SimpleCallContext *), Value (*setter)(SimpleCallContext *)) -{ - String *s = engine->newString(name); - defineAccessorProperty(s, getter, setter); -} - -void Object::defineAccessorProperty(String *name, Value (*getter)(SimpleCallContext *), Value (*setter)(SimpleCallContext *)) -{ - ExecutionEngine *v4 = engine(); - Property *p = insertMember(name, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); - if (getter) - p->setGetter(v4->newBuiltinFunction(v4->rootContext, name, getter)); - if (setter) - p->setSetter(v4->newBuiltinFunction(v4->rootContext, name, setter)); -} - -void Object::defineReadonlyProperty(ExecutionEngine *engine, const QString &name, Value value) -{ - defineReadonlyProperty(engine->newIdentifier(name), value); -} - -void Object::defineReadonlyProperty(String *name, Value value) -{ - Property *pd = insertMember(name, Attr_ReadOnly); - pd->value = value; -} - -void Object::markObjects(Managed *that) -{ - Object *o = static_cast(that); - if (o->prototype) - o->prototype->mark(); - - for (int i = 0; i < o->internalClass->size; ++i) { - const Property &pd = o->memberData[i]; - if (o->internalClass->propertyData[i].isData()) { - if (Managed *m = pd.value.asManaged()) - m->mark(); - } else { - if (pd.getter()) - pd.getter()->mark(); - if (pd.setter()) - pd.setter()->mark(); - } - } - o->markArrayObjects(); -} - -Property *Object::insertMember(String *s, PropertyAttributes attributes) -{ - uint idx; - internalClass = internalClass->addMember(s, attributes, &idx); - - if (idx >= memberDataAlloc) { - memberDataAlloc = qMax((uint)8, 2*memberDataAlloc); - Property *newMemberData = new Property[memberDataAlloc]; - memcpy(newMemberData, memberData, sizeof(Property)*idx); - memset(newMemberData + idx, 0, sizeof(Property)*(memberDataAlloc - idx)); - if (memberData != inlineProperties) - delete [] memberData; - memberData = newMemberData; - } - return memberData + idx; -} - -// Section 8.12.1 -Property *Object::__getOwnProperty__(String *name, PropertyAttributes *attrs) -{ - uint idx = name->asArrayIndex(); - if (idx != UINT_MAX) - return __getOwnProperty__(idx, attrs); - - uint member = internalClass->find(name); - if (member < UINT_MAX) { - if (attrs) - *attrs = internalClass->propertyData[member]; - return memberData + member; - } - - if (attrs) - *attrs = Attr_Invalid; - return 0; -} - -Property *Object::__getOwnProperty__(uint index, PropertyAttributes *attrs) -{ - uint pidx = propertyIndexFromArrayIndex(index); - if (pidx < UINT_MAX) { - Property *p = arrayData + pidx; - if (!arrayAttributes || arrayAttributes[pidx].isData()) { - if (attrs) - *attrs = arrayAttributes ? arrayAttributes[pidx] : PropertyAttributes(Attr_Data); - return p; - } else if (arrayAttributes[pidx].isAccessor()) { - if (attrs) - *attrs = arrayAttributes ? arrayAttributes[pidx] : PropertyAttributes(Attr_Accessor); - return p; - } - } - if (isStringObject()) { - if (attrs) - *attrs = Attr_NotConfigurable|Attr_NotWritable; - return static_cast(this)->getIndex(index); - } - - if (attrs) - *attrs = Attr_Invalid; - return 0; -} - -// Section 8.12.2 -Property *Object::__getPropertyDescriptor__(String *name, PropertyAttributes *attrs) const -{ - uint idx = name->asArrayIndex(); - if (idx != UINT_MAX) - return __getPropertyDescriptor__(idx); - - - const Object *o = this; - while (o) { - uint idx = o->internalClass->find(name); - if (idx < UINT_MAX) { - if (attrs) - *attrs = o->internalClass->propertyData[idx]; - return o->memberData + idx; - } - - o = o->prototype; - } - if (attrs) - *attrs = Attr_Invalid; - return 0; -} - -Property *Object::__getPropertyDescriptor__(uint index, PropertyAttributes *attrs) const -{ - const Object *o = this; - while (o) { - uint pidx = o->propertyIndexFromArrayIndex(index); - if (pidx < UINT_MAX) { - Property *p = o->arrayData + pidx; - if (!o->arrayAttributes || !o->arrayAttributes[pidx].isGeneric()) { - if (attrs) - *attrs = o->arrayAttributes ? o->arrayAttributes[pidx] : PropertyAttributes(Attr_Data); - return p; - } - } - if (o->isStringObject()) { - Property *p = static_cast(o)->getIndex(index); - if (p) { - if (attrs) - *attrs = (Attr_NotWritable|Attr_NotConfigurable); - return p; - } - } - o = o->prototype; - } - if (attrs) - *attrs = Attr_Invalid; - return 0; -} - -bool Object::__hasProperty__(String *name) const -{ - if (__getPropertyDescriptor__(name)) - return true; - return !query(name).isEmpty(); -} - -Value Object::get(Managed *m, String *name, bool *hasProperty) -{ - return static_cast(m)->internalGet(name, hasProperty); -} - -Value Object::getIndexed(Managed *m, uint index, bool *hasProperty) -{ - return static_cast(m)->internalGetIndexed(index, hasProperty); -} - -void Object::put(Managed *m, String *name, const Value &value) -{ - static_cast(m)->internalPut(name, value); -} - -void Object::putIndexed(Managed *m, uint index, const Value &value) -{ - static_cast(m)->internalPutIndexed(index, value); -} - -PropertyAttributes Object::query(const Managed *m, String *name) -{ - uint idx = name->asArrayIndex(); - if (idx != UINT_MAX) - return queryIndexed(m, idx); - - const Object *o = static_cast(m); - while (o) { - uint idx = o->internalClass->find(name); - if (idx < UINT_MAX) - return o->internalClass->propertyData[idx]; - - o = o->prototype; - } - return Attr_Invalid; -} - -PropertyAttributes Object::queryIndexed(const Managed *m, uint index) -{ - const Object *o = static_cast(m); - while (o) { - uint pidx = o->propertyIndexFromArrayIndex(index); - if (pidx < UINT_MAX) { - if (o->arrayAttributes) - return o->arrayAttributes[pidx]; - return Attr_Data; - } - if (o->isStringObject()) { - Property *p = static_cast(o)->getIndex(index); - if (p) - return Attr_Data; - } - o = o->prototype; - } - return Attr_Invalid; -} - -bool Object::deleteProperty(Managed *m, String *name) -{ - return static_cast(m)->internalDeleteProperty(name); -} - -bool Object::deleteIndexedProperty(Managed *m, uint index) -{ - return static_cast(m)->internalDeleteIndexedProperty(index); -} - -void Object::getLookup(Managed *m, Lookup *l, Value *result) -{ - Object *o = static_cast(m); - PropertyAttributes attrs; - Property *p = l->lookup(o, &attrs); - if (p) { - if (attrs.isData()) { - if (l->level == 0) - l->getter = Lookup::getter0; - else if (l->level == 1) - l->getter = Lookup::getter1; - else if (l->level == 2) - l->getter = Lookup::getter2; - if (result) - *result = p->value; - return; - } else { - if (l->level == 0) - l->getter = Lookup::getterAccessor0; - else if (l->level == 1) - l->getter = Lookup::getterAccessor1; - else if (l->level == 2) - l->getter = Lookup::getterAccessor2; - if (result) - *result = p->value; - Value res = o->getValue(p, attrs); - if (result) - *result = res; - return; - } - } else if (result) { - *result = Value::undefinedValue(); - } -} - -void Object::setLookup(Managed *m, Lookup *l, const Value &value) -{ - Object *o = static_cast(m); - - uint idx = o->internalClass->find(l->name); - if (!o->isArrayObject() || idx != ArrayObject::LengthPropertyIndex) { - if (idx != UINT_MAX && o->internalClass->propertyData[idx].isData() && o->internalClass->propertyData[idx].isWritable()) { - l->classList[0] = o->internalClass; - l->index = idx; - l->setter = Lookup::setter0; - o->memberData[idx].value = value; - return; - } - - if (idx != UINT_MAX) { - o->putValue(o->memberData + idx, o->internalClass->propertyData[idx], value); - return; - } - } - - o->put(l->name, value); -} - -Property *Object::advanceIterator(Managed *m, ObjectIterator *it, String **name, uint *index, PropertyAttributes *attrs) -{ - Object *o = static_cast(m); - *name = 0; - *index = UINT_MAX; - - if (!it->arrayIndex) - it->arrayNode = o->sparseArrayBegin(); - - // sparse arrays - if (it->arrayNode) { - while (it->arrayNode != o->sparseArrayEnd()) { - int k = it->arrayNode->key(); - uint pidx = it->arrayNode->value; - Property *p = o->arrayData + pidx; - it->arrayNode = it->arrayNode->nextNode(); - PropertyAttributes a = o->arrayAttributes ? o->arrayAttributes[pidx] : PropertyAttributes(Attr_Data); - if (!(it->flags & ObjectIterator::EnumerableOnly) || a.isEnumerable()) { - it->arrayIndex = k + 1; - *index = k; - if (attrs) - *attrs = a; - return p; - } - } - it->arrayNode = 0; - it->arrayIndex = UINT_MAX; - } - // dense arrays - while (it->arrayIndex < o->arrayDataLen) { - uint pidx = o->propertyIndexFromArrayIndex(it->arrayIndex); - Property *p = o->arrayData + pidx; - PropertyAttributes a = o->arrayAttributes ? o->arrayAttributes[pidx] : PropertyAttributes(Attr_Data); - ++it->arrayIndex; - if ((!o->arrayAttributes || !o->arrayAttributes[pidx].isGeneric()) - && (!(it->flags & ObjectIterator::EnumerableOnly) || a.isEnumerable())) { - *index = it->arrayIndex - 1; - if (attrs) - *attrs = a; - return p; - } - } - - while (it->memberIndex < o->internalClass->size) { - String *n = o->internalClass->nameMap.at(it->memberIndex); - assert(n); - - Property *p = o->memberData + it->memberIndex; - PropertyAttributes a = o->internalClass->propertyData[it->memberIndex]; - ++it->memberIndex; - if (!(it->flags & ObjectIterator::EnumerableOnly) || a.isEnumerable()) { - *name = n; - if (attrs) - *attrs = a; - return p; - } - } - - return 0; -} - -// Section 8.12.3 -Value Object::internalGet(String *name, bool *hasProperty) -{ - uint idx = name->asArrayIndex(); - if (idx != UINT_MAX) - return getIndexed(idx, hasProperty); - - name->makeIdentifier(); - - Object *o = this; - while (o) { - uint idx = o->internalClass->find(name); - if (idx < UINT_MAX) { - if (hasProperty) - *hasProperty = true; - return getValue(o->memberData + idx, o->internalClass->propertyData.at(idx)); - } - - o = o->prototype; - } - - if (hasProperty) - *hasProperty = false; - return Value::undefinedValue(); -} - -Value Object::internalGetIndexed(uint index, bool *hasProperty) -{ - Property *pd = 0; - PropertyAttributes attrs = Attr_Data; - Object *o = this; - while (o) { - uint pidx = o->propertyIndexFromArrayIndex(index); - if (pidx < UINT_MAX) { - if (!o->arrayAttributes || !o->arrayAttributes[pidx].isGeneric()) { - pd = o->arrayData + pidx; - if (o->arrayAttributes) - attrs = o->arrayAttributes[pidx]; - break; - } - } - if (o->isStringObject()) { - pd = static_cast(o)->getIndex(index); - if (pd) { - attrs = (Attr_NotWritable|Attr_NotConfigurable); - break; - } - } - o = o->prototype; - } - - if (pd) { - if (hasProperty) - *hasProperty = true; - return getValue(pd, attrs); - } - - if (hasProperty) - *hasProperty = false; - return Value::undefinedValue(); -} - - -// Section 8.12.5 -void Object::internalPut(String *name, const Value &value) -{ - uint idx = name->asArrayIndex(); - if (idx != UINT_MAX) - return putIndexed(idx, value); - - name->makeIdentifier(); - - uint member = internalClass->find(name); - Property *pd = 0; - PropertyAttributes attrs; - if (member < UINT_MAX) { - pd = memberData + member; - attrs = internalClass->propertyData[member]; - } - - // clause 1 - if (pd) { - if (attrs.isAccessor()) { - if (pd->setter()) - goto cont; - goto reject; - } else if (!attrs.isWritable()) - goto reject; - else if (isArrayObject() && name->isEqualTo(engine()->id_length)) { - bool ok; - uint l = value.asArrayLength(&ok); - if (!ok) - engine()->current->throwRangeError(value); - ok = setArrayLength(l); - if (!ok) - goto reject; - } else { - pd->value = value; - } - return; - } else if (!prototype) { - if (!extensible) - goto reject; - } else { - // clause 4 - if ((pd = prototype->__getPropertyDescriptor__(name, &attrs))) { - if (attrs.isAccessor()) { - if (!pd->setter()) - goto reject; - } else if (!extensible || !attrs.isWritable()) { - goto reject; - } - } else if (!extensible) { - goto reject; - } - } - - cont: - - // Clause 5 - if (pd && attrs.isAccessor()) { - assert(pd->setter() != 0); - - Value args[1]; - args[0] = value; - pd->setter()->call(Value::fromObject(this), args, 1); - return; - } - - { - Property *p = insertMember(name, Attr_Data); - p->value = value; - return; - } - - reject: - if (engine()->current->strictMode) { - QString message = QStringLiteral("Cannot assign to read-only property \""); - message += name->toQString(); - message += QLatin1Char('\"'); - engine()->current->throwTypeError(message); - } -} - -void Object::internalPutIndexed(uint index, const Value &value) -{ - Property *pd = 0; - PropertyAttributes attrs; - - uint pidx = propertyIndexFromArrayIndex(index); - if (pidx < UINT_MAX) { - if (arrayAttributes && arrayAttributes[pidx].isGeneric()) { - pidx = UINT_MAX; - } else { - pd = arrayData + pidx; - attrs = arrayAttributes ? arrayAttributes[pidx] : PropertyAttributes(Attr_Data); - } - } - - if (!pd && isStringObject()) { - pd = static_cast(this)->getIndex(index); - if (pd) - // not writable - goto reject; - } - - // clause 1 - if (pd) { - if (attrs.isAccessor()) { - if (pd->setter()) - goto cont; - goto reject; - } else if (!attrs.isWritable()) - goto reject; - else - pd->value = value; - return; - } else if (!prototype) { - if (!extensible) - goto reject; - } else { - // clause 4 - if ((pd = prototype->__getPropertyDescriptor__(index, &attrs))) { - if (attrs.isAccessor()) { - if (!pd->setter()) - goto reject; - } else if (!extensible || !attrs.isWritable()) { - goto reject; - } - } else if (!extensible) { - goto reject; - } - } - - cont: - - // Clause 5 - if (pd && attrs.isAccessor()) { - assert(pd->setter() != 0); - - Value args[1]; - args[0] = value; - pd->setter()->call(Value::fromObject(this), args, 1); - return; - } - - arraySet(index, value); - return; - - reject: - if (engine()->current->strictMode) - engine()->current->throwTypeError(); -} - -// Section 8.12.7 -bool Object::internalDeleteProperty(String *name) -{ - uint idx = name->asArrayIndex(); - if (idx != UINT_MAX) - return deleteIndexedProperty(idx); - - name->makeIdentifier(); - - uint memberIdx = internalClass->find(name); - if (memberIdx != UINT_MAX) { - if (internalClass->propertyData[memberIdx].isConfigurable()) { - internalClass->removeMember(this, name->identifier); - memmove(memberData + memberIdx, memberData + memberIdx + 1, (internalClass->size - memberIdx)*sizeof(Property)); - return true; - } - if (engine()->current->strictMode) - engine()->current->throwTypeError(); - return false; - } - - return true; -} - -bool Object::internalDeleteIndexedProperty(uint index) -{ - uint pidx = propertyIndexFromArrayIndex(index); - if (pidx == UINT_MAX) - return true; - if (arrayAttributes && arrayAttributes[pidx].isGeneric()) - return true; - - if (!arrayAttributes || arrayAttributes[pidx].isConfigurable()) { - arrayData[pidx].value = Value::undefinedValue(); - if (!arrayAttributes) - ensureArrayAttributes(); - arrayAttributes[pidx].clear(); - if (sparseArray) { - arrayData[pidx].value.int_32 = arrayFreeList; - arrayFreeList = pidx; - } - return true; - } - - if (engine()->current->strictMode) - engine()->current->throwTypeError(); - return false; -} - -// Section 8.12.9 -bool Object::__defineOwnProperty__(ExecutionContext *ctx, String *name, const Property &p, PropertyAttributes attrs) -{ - uint idx = name->asArrayIndex(); - if (idx != UINT_MAX) - return __defineOwnProperty__(ctx, idx, p, attrs); - - name->makeIdentifier(); - - Property *current; - PropertyAttributes *cattrs; - - if (isArrayObject() && name->isEqualTo(ctx->engine->id_length)) { - assert(ArrayObject::LengthPropertyIndex == internalClass->find(ctx->engine->id_length)); - Property *lp = memberData + ArrayObject::LengthPropertyIndex; - cattrs = internalClass->propertyData.data() + ArrayObject::LengthPropertyIndex; - if (attrs.isEmpty() || p.isSubset(attrs, *lp, *cattrs)) - return true; - if (!cattrs->isWritable() || attrs.type() == PropertyAttributes::Accessor || attrs.isConfigurable() || attrs.isEnumerable()) - goto reject; - bool succeeded = true; - if (attrs.type() == PropertyAttributes::Data) { - bool ok; - uint l = p.value.asArrayLength(&ok); - if (!ok) - ctx->throwRangeError(p.value); - succeeded = setArrayLength(l); - } - if (attrs.hasWritable() && !attrs.isWritable()) - cattrs->setWritable(false); - if (!succeeded) - goto reject; - return true; - } - - // Clause 1 - { - uint member = internalClass->find(name); - current = (member < UINT_MAX) ? memberData + member : 0; - cattrs = internalClass->propertyData.data() + member; - } - - if (!current) { - // clause 3 - if (!extensible) - goto reject; - // clause 4 - Property *pd = insertMember(name, attrs); - *pd = p; - pd->fullyPopulated(&attrs); - return true; - } - - return __defineOwnProperty__(ctx, current, name, p, attrs); -reject: - if (ctx->strictMode) - ctx->throwTypeError(); - return false; -} - -bool Object::__defineOwnProperty__(ExecutionContext *ctx, uint index, const Property &p, PropertyAttributes attrs) -{ - Property *current = 0; - - // 15.4.5.1, 4b - if (isArrayObject() && index >= arrayLength() && !internalClass->propertyData[ArrayObject::LengthPropertyIndex].isWritable()) - goto reject; - - if (isNonStrictArgumentsObject) - return static_cast(this)->defineOwnProperty(ctx, index, p, attrs); - - // Clause 1 - { - uint pidx = propertyIndexFromArrayIndex(index); - if (pidx < UINT_MAX && (!arrayAttributes || !arrayAttributes[pidx].isGeneric())) - current = arrayData + pidx; - if (!current && isStringObject()) - current = static_cast(this)->getIndex(index); - } - - if (!current) { - // clause 3 - if (!extensible) - goto reject; - // clause 4 - Property *pd = arrayInsert(index, attrs); - *pd = p; - pd->fullyPopulated(&attrs); - return true; - } - - return __defineOwnProperty__(ctx, current, 0 /*member*/, p, attrs); -reject: - if (ctx->strictMode) - ctx->throwTypeError(); - return false; -} - -bool Object::__defineOwnProperty__(ExecutionContext *ctx, Property *current, String *member, const Property &p, PropertyAttributes attrs) -{ - // clause 5 - if (attrs.isEmpty()) - return true; - - PropertyAttributes cattrs = Attr_Data; - if (member) - cattrs = internalClass->propertyData[current - memberData]; - else if (arrayAttributes) - cattrs = arrayAttributes[current - arrayData]; - - // clause 6 - if (p.isSubset(attrs, *current, cattrs)) - return true; - - // clause 7 - if (!cattrs.isConfigurable()) { - if (attrs.isConfigurable()) - goto reject; - if (attrs.hasEnumerable() && attrs.isEnumerable() != cattrs.isEnumerable()) - goto reject; - } - - // clause 8 - if (attrs.isGeneric()) - goto accept; - - // clause 9 - if (cattrs.isData() != attrs.isData()) { - // 9a - if (!cattrs.isConfigurable()) - goto reject; - if (cattrs.isData()) { - // 9b - cattrs.setType(PropertyAttributes::Accessor); - cattrs.clearWritable(); - current->setGetter(0); - current->setSetter(0); - } else { - // 9c - cattrs.setType(PropertyAttributes::Data); - cattrs.setWritable(false); - current->value = Value::undefinedValue(); - } - } else if (cattrs.isData() && attrs.isData()) { // clause 10 - if (!cattrs.isConfigurable() && !cattrs.isWritable()) { - if (attrs.isWritable() || !current->value.sameValue(p.value)) - goto reject; - } - } else { // clause 10 - assert(cattrs.isAccessor() && attrs.isAccessor()); - if (!cattrs.isConfigurable()) { - if (p.getter() && !(current->getter() == p.getter() || (!current->getter() && (quintptr)p.getter() == 0x1))) - goto reject; - if (p.setter() && !(current->setter() == p.setter() || (!current->setter() && (quintptr)p.setter() == 0x1))) - goto reject; - } - } - - accept: - - current->merge(cattrs, p, attrs); - if (member) { - internalClass = internalClass->changeMember(member, cattrs); - } else { - if (cattrs != Attr_Data) - ensureArrayAttributes(); - if (arrayAttributes) - arrayAttributes[current - arrayData] = cattrs; - } - return true; - reject: - if (ctx->strictMode) - ctx->throwTypeError(); - return false; -} - - -bool Object::__defineOwnProperty__(ExecutionContext *ctx, const QString &name, const Property &p, PropertyAttributes attrs) -{ - return __defineOwnProperty__(ctx, ctx->engine->newString(name), p, attrs); -} - - -void Object::copyArrayData(Object *other) -{ - arrayReserve(other->arrayDataLen); - arrayDataLen = other->arrayDataLen; - memcpy(arrayData, other->arrayData, arrayDataLen*sizeof(Property)); - arrayOffset = 0; - if (other->sparseArray) { - sparseArray = new SparseArray(*other->sparseArray); - arrayFreeList = other->arrayFreeList; - } - if (isArrayObject()) - setArrayLengthUnchecked(other->arrayLength()); -} - - -Value Object::arrayIndexOf(Value v, uint fromIndex, uint endIndex, ExecutionContext *ctx, Object *o) -{ - bool protoHasArray = false; - Object *p = o; - while ((p = p->prototype)) - if (p->arrayDataLen) - protoHasArray = true; - - if (protoHasArray || o->arrayAttributes) { - // lets be safe and slow - for (uint i = fromIndex; i < endIndex; ++i) { - bool exists; - Value value = o->getIndexed(i, &exists); - if (exists && __qmljs_strict_equal(value, v)) - return Value::fromDouble(i); - } - } else if (sparseArray) { - for (SparseArrayNode *n = sparseArray->lowerBound(fromIndex); n != sparseArray->end() && n->key() < endIndex; n = n->nextNode()) { - Value value = o->getValue(arrayData + n->value, arrayAttributes ? arrayAttributes[n->value] : Attr_Data); - if (__qmljs_strict_equal(value, v)) - return Value::fromDouble(n->key()); - } - } else { - if ((int) endIndex > arrayDataLen) - endIndex = arrayDataLen; - Property *pd = arrayData; - Property *end = pd + endIndex; - pd += fromIndex; - while (pd < end) { - if (!arrayAttributes || !arrayAttributes[pd - arrayData].isGeneric()) { - Value value = o->getValue(pd, arrayAttributes ? arrayAttributes[pd - arrayData] : Attr_Data); - if (__qmljs_strict_equal(value, v)) - return Value::fromDouble(pd - arrayData); - } - ++pd; - } - } - return Value::fromInt32(-1); -} - -void Object::arrayConcat(const ArrayObject *other) -{ - int newLen = arrayDataLen + other->arrayLength(); - if (other->sparseArray) - initSparse(); - // ### copy attributes as well! - if (sparseArray) { - if (other->sparseArray) { - for (const SparseArrayNode *it = other->sparseArray->begin(); it != other->sparseArray->end(); it = it->nextNode()) - arraySet(arrayDataLen + it->key(), other->arrayData + it->value); - } else { - int oldSize = arrayDataLen; - arrayReserve(oldSize + other->arrayLength()); - memcpy(arrayData + oldSize, other->arrayData, other->arrayLength()*sizeof(Property)); - if (arrayAttributes) - std::fill(arrayAttributes + oldSize, arrayAttributes + oldSize + other->arrayLength(), PropertyAttributes(Attr_Data)); - for (uint i = 0; i < other->arrayLength(); ++i) { - SparseArrayNode *n = sparseArray->insert(arrayDataLen + i); - n->value = oldSize + i; - } - } - } else { - int oldSize = arrayLength(); - arrayReserve(oldSize + other->arrayDataLen); - if (oldSize > arrayDataLen) { - ensureArrayAttributes(); - std::fill(arrayAttributes + arrayDataLen, arrayAttributes + oldSize, PropertyAttributes()); - } - arrayDataLen = oldSize + other->arrayDataLen; - if (other->arrayAttributes) { - for (int i = 0; i < arrayDataLen; ++i) { - bool exists; - arrayData[oldSize + i].value = const_cast(other)->getIndexed(i, &exists); - if (arrayAttributes) - arrayAttributes[oldSize + i] = Attr_Data; - if (!exists) { - ensureArrayAttributes(); - arrayAttributes[oldSize + i].clear(); - } - } - } else { - memcpy(arrayData + oldSize, other->arrayData, other->arrayDataLen*sizeof(Property)); - if (arrayAttributes) - std::fill(arrayAttributes + oldSize, arrayAttributes + oldSize + other->arrayDataLen, PropertyAttributes(Attr_Data)); - } - } - setArrayLengthUnchecked(newLen); -} - -void Object::arraySort(ExecutionContext *context, Object *thisObject, const Value &comparefn, uint len) -{ - if (!arrayDataLen) - return; - - if (sparseArray) { - context->throwUnimplemented("Object::sort unimplemented for sparse arrays"); - return; - } - - if (len > arrayDataLen) - len = arrayDataLen; - - // The spec says the sorting goes through a series of get,put and delete operations. - // this implies that the attributes don't get sorted around. - // behavior of accessor properties is implementation defined. We simply turn them all - // into data properties and then sort. This is in line with the sentence above. - if (arrayAttributes) { - for (uint i = 0; i < len; i++) { - if (arrayAttributes[i].isGeneric()) { - while (--len > i) - if (!arrayAttributes[len].isGeneric()) - break; - arrayData[i].value = getValue(arrayData + len, arrayAttributes[len]); - arrayAttributes[i] = Attr_Data; - arrayAttributes[len].clear(); - } else if (arrayAttributes[i].isAccessor()) { - arrayData[i].value = getValue(arrayData + i, arrayAttributes[i]); - arrayAttributes[i] = Attr_Data; - } - } - } - - ArrayElementLessThan lessThan(context, thisObject, comparefn); - - Property *begin = arrayData; - // We deliberately choose qSort over std::sort here, because with - // MSVC in debug builds, std::sort has an ASSERT() that verifies - // that the return values of lessThan are perfectly consistent - // and aborts otherwise. We do not want JavaScript to easily crash - // the entire application and therefore choose qSort, which doesn't - // have this property. - qSort(begin, begin + len, lessThan); -} - - -void Object::initSparse() -{ - if (!sparseArray) { - sparseArray = new SparseArray; - for (int i = 0; i < arrayDataLen; ++i) { - if (!arrayAttributes || !arrayAttributes[i].isGeneric()) { - SparseArrayNode *n = sparseArray->insert(i); - n->value = i + arrayOffset; - } - } - - uint off = arrayOffset; - if (!arrayOffset) { - arrayFreeList = arrayDataLen; - } else { - arrayFreeList = 0; - arrayData -= off; - arrayAlloc += off; - int o = off; - for (int i = 0; i < o - 1; ++i) { - arrayData[i].value = Value::fromInt32(i + 1); - } - arrayData[o - 1].value = Value::fromInt32(arrayDataLen + off); - } - for (int i = arrayDataLen + off; i < arrayAlloc; ++i) { - arrayData[i].value = Value::fromInt32(i + 1); - } - } -} - -void Object::arrayReserve(uint n) -{ - if (n < 8) - n = 8; - if (n >= arrayAlloc) { - uint off; - if (sparseArray) { - assert(arrayFreeList == arrayAlloc); - // ### FIXME - arrayDataLen = arrayAlloc; - off = 0; - } else { - off = arrayOffset; - } - arrayAlloc = qMax(n, 2*arrayAlloc); - Property *newArrayData = new Property[arrayAlloc]; - if (arrayData) { - memcpy(newArrayData, arrayData, sizeof(Property)*arrayDataLen); - delete [] (arrayData - off); - } - arrayData = newArrayData; - if (sparseArray) { - for (uint i = arrayFreeList; i < arrayAlloc; ++i) { - arrayData[i].value = Value::emptyValue(); - arrayData[i].value = Value::fromInt32(i + 1); - } - } else { - arrayOffset = 0; - } - - if (arrayAttributes) { - PropertyAttributes *newAttrs = new PropertyAttributes[arrayAlloc]; - memcpy(newAttrs, arrayAttributes, sizeof(PropertyAttributes)*arrayDataLen); - delete [] (arrayAttributes - off); - - arrayAttributes = newAttrs; - if (sparseArray) { - for (uint i = arrayFreeList; i < arrayAlloc; ++i) - arrayAttributes[i] = Attr_Invalid; - } - } - } -} - -void Object::ensureArrayAttributes() -{ - if (arrayAttributes) - return; - - arrayAttributes = new PropertyAttributes[arrayAlloc]; - for (uint i = 0; i < arrayDataLen; ++i) - arrayAttributes[i] = Attr_Data; - for (uint i = arrayDataLen; i < arrayAlloc; ++i) - arrayAttributes[i] = Attr_Invalid; -} - - -bool Object::setArrayLength(uint newLen) { - assert(isArrayObject()); - const Property *lengthProperty = memberData + ArrayObject::LengthPropertyIndex; - if (lengthProperty && !internalClass->propertyData[ArrayObject::LengthPropertyIndex].isWritable()) - return false; - uint oldLen = arrayLength(); - bool ok = true; - if (newLen < oldLen) { - if (sparseArray) { - SparseArrayNode *begin = sparseArray->lowerBound(newLen); - if (begin != sparseArray->end()) { - SparseArrayNode *it = sparseArray->end()->previousNode(); - while (1) { - Property &pd = arrayData[it->value]; - if (arrayAttributes) { - if (!arrayAttributes[it->value].isConfigurable()) { - ok = false; - newLen = it->key() + 1; - break; - } else { - arrayAttributes[it->value].clear(); - } - } - pd.value.tag = Value::_Empty_Type; - pd.value.int_32 = arrayFreeList; - arrayFreeList = it->value; - bool brk = (it == begin); - SparseArrayNode *prev = it->previousNode(); - sparseArray->erase(it); - if (brk) - break; - it = prev; - } - } - } else { - Property *it = arrayData + arrayDataLen; - const Property *begin = arrayData + newLen; - while (--it >= begin) { - if (arrayAttributes) { - if (!arrayAttributes[it - arrayData].isEmpty() && !arrayAttributes[it - arrayData].isConfigurable()) { - ok = false; - newLen = it - arrayData + 1; - break; - } else { - arrayAttributes[it - arrayData].clear(); - } - it->value = Value::emptyValue(); - } - } - arrayDataLen = newLen; - } - } else { - if (newLen >= 0x100000) - initSparse(); - } - setArrayLengthUnchecked(newLen); - return ok; -} - -void Object::markArrayObjects() const -{ - for (uint i = 0; i < arrayDataLen; ++i) { - const Property &pd = arrayData[i]; - if (!arrayAttributes || arrayAttributes[i].isData()) { - if (Managed *m = pd.value.asManaged()) - m->mark(); - } else if (arrayAttributes[i].isAccessor()) { - if (pd.getter()) - pd.getter()->mark(); - if (pd.setter()) - pd.setter()->mark(); - } - } -} - -ArrayObject::ArrayObject(ExecutionEngine *engine, const QStringList &list) - : Object(engine) -{ - init(engine); - - // Converts a QStringList to JS. - // The result is a new Array object with length equal to the length - // of the QStringList, and the elements being the QStringList's - // elements converted to JS Strings. - int len = list.count(); - arrayReserve(len); - for (int ii = 0; ii < len; ++ii) - arrayData[ii].value = Value::fromString(engine->newString(list.at(ii))); - setArrayLengthUnchecked(len); -} - -void ArrayObject::init(ExecutionEngine *engine) -{ - type = Type_ArrayObject; - internalClass = engine->arrayClass; - - memberData = new Property[4]; - memberData[LengthPropertyIndex].value = Value::fromInt32(0); -} - -QStringList ArrayObject::toQStringList() const -{ - QStringList result; - - QV4::ExecutionEngine *engine = internalClass->engine; - - uint32_t length = arrayLength(); - for (uint32_t i = 0; i < length; ++i) - result.append(const_cast(this)->getIndexed(i).toString(engine->current)->toQString()); - return result; -} - - -DEFINE_MANAGED_VTABLE(ForEachIteratorObject); - -void ForEachIteratorObject::markObjects(Managed *that) -{ - ForEachIteratorObject *o = static_cast(that); - Object::markObjects(that); - if (o->it.object) - o->it.object->mark(); -} diff --git a/src/qml/qml/v4/qv4object_p.h b/src/qml/qml/v4/qv4object_p.h deleted file mode 100644 index 8d064adcaa..0000000000 --- a/src/qml/qml/v4/qv4object_p.h +++ /dev/null @@ -1,434 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QMLJS_OBJECTS_H -#define QMLJS_OBJECTS_H - -#include "qv4global_p.h" -#include "qv4runtime_p.h" -#include "qv4engine_p.h" -#include "qv4context_p.h" -#include "qv4sparsearray_p.h" -#include "qv4string_p.h" -#include "qv4codegen_p.h" -#include "qv4isel_p.h" -#include "qv4managed_p.h" -#include "qv4property_p.h" -#include "qv4internalclass_p.h" -#include "qv4objectiterator_p.h" - -#include -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE - -namespace QV4 { - -struct Function; -struct Lookup; -struct Object; -struct ObjectIterator; -struct BooleanObject; -struct NumberObject; -struct StringObject; -struct ArrayObject; -struct DateObject; -struct FunctionObject; -struct RegExpObject; -struct ErrorObject; -struct ArgumentsObject; -struct ExecutionContext; -struct CallContext; -struct ExecutionEngine; -class MemoryManager; - -struct ObjectPrototype; -struct StringPrototype; -struct NumberPrototype; -struct BooleanPrototype; -struct ArrayPrototype; -struct FunctionPrototype; -struct DatePrototype; -struct RegExpPrototype; -struct ErrorPrototype; -struct EvalErrorPrototype; -struct RangeErrorPrototype; -struct ReferenceErrorPrototype; -struct SyntaxErrorPrototype; -struct TypeErrorPrototype; -struct URIErrorPrototype; - -typedef Value (*PropertyEnumeratorFunction)(Object *object); -typedef PropertyAttributes (*PropertyQueryFunction)(const Object *object, String *name); - -struct Q_QML_EXPORT Object: Managed { - Object *prototype; - uint memberDataAlloc; - Property *memberData; - - union { - uint arrayFreeList; - uint arrayOffset; - }; - uint arrayDataLen; - uint arrayAlloc; - PropertyAttributes *arrayAttributes; - Property *arrayData; - SparseArray *sparseArray; - - enum { - InlinePropertySize = 4 - }; - Property inlineProperties[InlinePropertySize]; - - Object(ExecutionEngine *engine); - Object(ExecutionContext *context); - Object(ExecutionEngine *engine, InternalClass *internalClass); - ~Object(); - - Property *__getOwnProperty__(String *name, PropertyAttributes *attrs = 0); - Property *__getOwnProperty__(uint index, PropertyAttributes *attrs = 0); - - Property *__getPropertyDescriptor__(String *name, PropertyAttributes *attrs = 0) const; - Property *__getPropertyDescriptor__(uint index, PropertyAttributes *attrs = 0) const; - - bool __hasProperty__(String *name) const; - bool __hasProperty__(uint index) const { - return __getPropertyDescriptor__(index); - } - - bool __defineOwnProperty__(ExecutionContext *ctx, Property *current, String *member, const Property &p, PropertyAttributes attrs); - bool __defineOwnProperty__(ExecutionContext *ctx, String *name, const Property &p, PropertyAttributes attrs); - bool __defineOwnProperty__(ExecutionContext *ctx, uint index, const Property &p, PropertyAttributes attrs); - bool __defineOwnProperty__(ExecutionContext *ctx, const QString &name, const Property &p, PropertyAttributes attrs); - - // - // helpers - // - void put(ExecutionContext *ctx, const QString &name, const Value &value); - - static Value getValue(const Value &thisObject, const Property *p, PropertyAttributes attrs); - Value getValue(const Property *p, PropertyAttributes attrs) const { - return getValue(Value::fromObject(const_cast(this)), p, attrs); - } - - void putValue(Property *pd, PropertyAttributes attrs, const Value &value); - - void inplaceBinOp(ExecutionContext *, BinOp op, String *name, const Value &rhs); - void inplaceBinOp(ExecutionContext *ctx, BinOp op, const Value &index, const Value &rhs); - void inplaceBinOp(ExecutionContext *ctx, BinOpContext op, String *name, const Value &rhs); - void inplaceBinOp(ExecutionContext *ctx, BinOpContext op, const Value &index, const Value &rhs); - - /* The spec default: Writable: true, Enumerable: false, Configurable: true */ - void defineDefaultProperty(String *name, Value value); - void defineDefaultProperty(ExecutionContext *context, const QString &name, Value value); - void defineDefaultProperty(ExecutionEngine *engine, const QString &name, Value value); - void defineDefaultProperty(ExecutionContext *context, const QString &name, Value (*code)(SimpleCallContext *), int count = 0); - void defineDefaultProperty(ExecutionEngine *engine, const QString &name, Value (*code)(SimpleCallContext *), int count = 0); - void defineAccessorProperty(ExecutionEngine *engine, const QString &name, Value (*getter)(SimpleCallContext *), Value (*setter)(SimpleCallContext *)); - void defineAccessorProperty(String *name, Value (*getter)(SimpleCallContext *), Value (*setter)(SimpleCallContext *)); - /* Fixed: Writable: false, Enumerable: false, Configurable: false */ - void defineReadonlyProperty(ExecutionEngine *engine, const QString &name, Value value); - void defineReadonlyProperty(String *name, Value value); - - Property *insertMember(String *s, PropertyAttributes attributes); - - inline ExecutionEngine *engine() const { return internalClass->engine; } - - // Array handling - - uint allocArrayValue() { - uint idx = arrayFreeList; - if (arrayAlloc <= arrayFreeList) - arrayReserve(arrayAlloc + 1); - arrayFreeList = arrayData[arrayFreeList].value.uint_32; - if (arrayAttributes) - arrayAttributes[idx].setType(PropertyAttributes::Data); - return idx; - } - - uint allocArrayValue(Value v) { - uint idx = allocArrayValue(); - Property *pd = &arrayData[idx]; - pd->value = v; - return idx; - } - void freeArrayValue(int idx) { - Property &pd = arrayData[idx]; - pd.value.tag = Value::_Empty_Type; - pd.value.int_32 = arrayFreeList; - arrayFreeList = idx; - if (arrayAttributes) - arrayAttributes[idx].clear(); - } - - void getArrayHeadRoom() { - assert(!sparseArray && !arrayOffset); - arrayOffset = qMax(arrayDataLen >> 2, (uint)16); - Property *newArray = new Property[arrayOffset + arrayAlloc]; - memcpy(newArray + arrayOffset, arrayData, arrayDataLen*sizeof(Property)); - delete [] arrayData; - arrayData = newArray + arrayOffset; - if (arrayAttributes) { - PropertyAttributes *newAttrs = new PropertyAttributes[arrayOffset + arrayAlloc]; - memcpy(newAttrs + arrayOffset, arrayAttributes, arrayDataLen*sizeof(PropertyAttributes)); - delete [] arrayAttributes; - arrayAttributes = newAttrs + arrayOffset; - } - } - -public: - void copyArrayData(Object *other); - void initSparse(); - - uint arrayLength() const; - bool setArrayLength(uint newLen); - - void setArrayLengthUnchecked(uint l); - - Property *arrayInsert(uint index, PropertyAttributes attributes = Attr_Data) { - - Property *pd; - if (!sparseArray && (index < 0x1000 || index < arrayDataLen + (arrayDataLen >> 2))) { - if (index >= arrayAlloc) - arrayReserve(index + 1); - if (index >= arrayDataLen) { - ensureArrayAttributes(); - for (uint i = arrayDataLen; i < index; ++i) - arrayAttributes[i].clear(); - arrayDataLen = index + 1; - } - pd = arrayData + index; - } else { - initSparse(); - SparseArrayNode *n = sparseArray->insert(index); - if (n->value == UINT_MAX) - n->value = allocArrayValue(); - pd = arrayData + n->value; - } - if (index >= arrayLength()) - setArrayLengthUnchecked(index + 1); - if (arrayAttributes || attributes != Attr_Data) { - if (!arrayAttributes) - ensureArrayAttributes(); - attributes.resolve(); - arrayAttributes[pd - arrayData] = attributes; - } - return pd; - } - - void arraySet(uint index, const Property *pd) { - *arrayInsert(index) = *pd; - } - - void arraySet(uint index, Value value) { - Property *pd = arrayInsert(index); - pd->value = value; - } - - uint propertyIndexFromArrayIndex(uint index) const - { - if (!sparseArray) { - if (index >= arrayDataLen) - return UINT_MAX; - return index; - } else { - SparseArrayNode *n = sparseArray->findNode(index); - if (!n) - return UINT_MAX; - return n->value; - } - } - - Property *arrayAt(uint index) const { - uint pidx = propertyIndexFromArrayIndex(index); - if (pidx == UINT_MAX) - return 0; - return arrayData + pidx; - } - - Property *nonSparseArrayAt(uint index) const { - if (sparseArray) - return 0; - if (index >= arrayDataLen) - return 0; - return arrayData + index; - } - - void markArrayObjects() const; - - void push_back(Value v) { - uint idx = arrayLength(); - if (!sparseArray) { - if (idx >= arrayAlloc) - arrayReserve(idx + 1); - arrayData[idx].value = v; - arrayDataLen = idx + 1; - } else { - uint idx = allocArrayValue(v); - sparseArray->push_back(idx, arrayLength()); - } - setArrayLengthUnchecked(idx + 1); - } - - SparseArrayNode *sparseArrayBegin() { return sparseArray ? sparseArray->begin() : 0; } - SparseArrayNode *sparseArrayEnd() { return sparseArray ? sparseArray->end() : 0; } - - void arrayConcat(const ArrayObject *other); - void arraySort(ExecutionContext *context, Object *thisObject, const Value &comparefn, uint arrayDataLen); - Value arrayIndexOf(Value v, uint fromIndex, uint arrayDataLen, ExecutionContext *ctx, Object *o); - - void arrayReserve(uint n); - void ensureArrayAttributes(); - - inline Value get(String *name, bool *hasProperty = 0) - { return vtbl->get(this, name, hasProperty); } - inline Value getIndexed(uint idx, bool *hasProperty = 0) - { return vtbl->getIndexed(this, idx, hasProperty); } - inline void put(String *name, const Value &v) - { vtbl->put(this, name, v); } - inline void putIndexed(uint idx, const Value &v) - { vtbl->putIndexed(this, idx, v); } - using Managed::get; - using Managed::getIndexed; - using Managed::put; - using Managed::putIndexed; - using Managed::query; - using Managed::queryIndexed; - using Managed::deleteProperty; - using Managed::deleteIndexedProperty; - using Managed::getLookup; - using Managed::setLookup; - using Managed::advanceIterator; -protected: - static const ManagedVTable static_vtbl; - static void destroy(Managed *that); - static void markObjects(Managed *that); - static Value get(Managed *m, String *name, bool *hasProperty); - static Value getIndexed(Managed *m, uint index, bool *hasProperty); - static void put(Managed *m, String *name, const Value &value); - static void putIndexed(Managed *m, uint index, const Value &value); - static PropertyAttributes query(const Managed *m, String *name); - static PropertyAttributes queryIndexed(const Managed *m, uint index); - static bool deleteProperty(Managed *m, String *name); - static bool deleteIndexedProperty(Managed *m, uint index); - static void getLookup(Managed *m, Lookup *l, Value *result); - static void setLookup(Managed *m, Lookup *l, const Value &v); - static Property *advanceIterator(Managed *m, ObjectIterator *it, String **name, uint *index, PropertyAttributes *attributes); - - -private: - Value internalGet(String *name, bool *hasProperty); - Value internalGetIndexed(uint index, bool *hasProperty); - void internalPut(String *name, const Value &value); - void internalPutIndexed(uint index, const Value &value); - bool internalDeleteProperty(String *name); - bool internalDeleteIndexedProperty(uint index); - - friend struct ObjectIterator; - friend struct ObjectPrototype; -}; - -struct ForEachIteratorObject: Object { - Q_MANAGED - ObjectIterator it; - ForEachIteratorObject(ExecutionContext *ctx, Object *o) - : Object(ctx->engine), it(o, ObjectIterator::EnumerableOnly|ObjectIterator::WithProtoChain) { - vtbl = &static_vtbl; - type = Type_ForeachIteratorObject; - } - - Value nextPropertyName() { return it.nextPropertyNameAsString(); } - -protected: - static void markObjects(Managed *that); -}; - -struct BooleanObject: Object { - Value value; - BooleanObject(ExecutionEngine *engine, const Value &value): Object(engine), value(value) { type = Type_BooleanObject; } -}; - -struct NumberObject: Object { - Value value; - NumberObject(ExecutionEngine *engine, const Value &value): Object(engine), value(value) { type = Type_NumberObject; } -}; - -struct ArrayObject: Object { - enum { - LengthPropertyIndex = 0 - }; - - ArrayObject(ExecutionEngine *engine) : Object(engine) { init(engine); } - ArrayObject(ExecutionEngine *engine, const QStringList &list); - void init(ExecutionEngine *engine); - - QStringList toQStringList() const; -}; - -inline uint Object::arrayLength() const -{ - if (isArrayObject()) { - // length is always the first property of an array - Value v = memberData[ArrayObject::LengthPropertyIndex].value; - if (v.isInteger()) - return v.integerValue(); - return Value::toUInt32(v.doubleValue()); - } - return 0; -} - -inline void Object::setArrayLengthUnchecked(uint l) -{ - if (isArrayObject()) { - // length is always the first property of an array - Property &lengthProperty = memberData[ArrayObject::LengthPropertyIndex]; - lengthProperty.value = Value::fromUInt32(l); - } -} - -} - -QT_END_NAMESPACE - -#endif // QMLJS_OBJECTS_H diff --git a/src/qml/qml/v4/qv4objectiterator.cpp b/src/qml/qml/v4/qv4objectiterator.cpp deleted file mode 100644 index a89bfdb797..0000000000 --- a/src/qml/qml/v4/qv4objectiterator.cpp +++ /dev/null @@ -1,129 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#include "qv4objectiterator_p.h" -#include "qv4object_p.h" -#include "qv4stringobject_p.h" -#include "qv4identifier_p.h" - -using namespace QV4; - -ObjectIterator::ObjectIterator(Object *o, uint flags) - : object(o) - , current(o) - , arrayNode(0) - , arrayIndex(0) - , memberIndex(0) - , flags(flags) -{ - tmpDynamicProperty.value = Value::undefinedValue(); -} - -Property *ObjectIterator::next(String **name, uint *index, PropertyAttributes *attrs) -{ - Property *p = 0; - *name = 0; - *index = UINT_MAX; - while (1) { - if (!current) - break; - - while (p = current->advanceIterator(this, name, index, attrs)) { - // check the property is not already defined earlier in the proto chain - if (current != object) { - Property *pp; - if (*name) { - pp = object->__getPropertyDescriptor__(*name); - } else { - assert (*index != UINT_MAX); - pp = object->__getPropertyDescriptor__(*index); - } - if (pp != p) - continue; - } - return p; - } - - if (flags & WithProtoChain) - current = current->prototype; - else - current = 0; - - arrayIndex = 0; - memberIndex = 0; - } - return 0; -} - -Value ObjectIterator::nextPropertyName(Value *value) -{ - PropertyAttributes attrs; - uint index; - String *name; - Property *p = next(&name, &index, &attrs); - if (!p) - return Value::nullValue(); - - if (value) - *value = object->getValue(p, attrs); - - if (name) - return Value::fromString(name); - assert(index < UINT_MAX); - return Value::fromDouble(index); -} - -Value ObjectIterator::nextPropertyNameAsString(Value *value) -{ - PropertyAttributes attrs; - uint index; - String *name; - Property *p = next(&name, &index, &attrs); - if (!p) - return Value::nullValue(); - - if (value) - *value = object->getValue(p, attrs); - - if (name) - return Value::fromString(name); - assert(index < UINT_MAX); - return Value::fromString(object->engine()->newString(QString::number(index))); -} diff --git a/src/qml/qml/v4/qv4objectiterator_p.h b/src/qml/qml/v4/qv4objectiterator_p.h deleted file mode 100644 index 95439397f5..0000000000 --- a/src/qml/qml/v4/qv4objectiterator_p.h +++ /dev/null @@ -1,87 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QV4OBJECTITERATOR_H -#define QV4OBJECTITERATOR_H - -#include "qv4global_p.h" -#include "qv4property_p.h" - -QT_BEGIN_NAMESPACE - -namespace QV4 { - -struct SparseArrayNode; -struct Object; -struct ArrayObject; -struct PropertyAttributes; -struct ExecutionContext; -struct Property; -struct String; -struct InternalClass; - -struct Q_QML_EXPORT ObjectIterator -{ - enum Flags { - NoFlags = 0, - EnumerableOnly = 0x1, - WithProtoChain = 0x2, - }; - - Object *object; - Object *current; - SparseArrayNode *arrayNode; - uint arrayIndex; - uint memberIndex; - uint flags; - - Property tmpDynamicProperty; - - ObjectIterator(Object *o, uint flags); - Property *next(String **name, uint *index, PropertyAttributes *attributes = 0); - Value nextPropertyName(Value *value = 0); - Value nextPropertyNameAsString(Value *value = 0); -}; - -} - -QT_END_NAMESPACE - -#endif diff --git a/src/qml/qml/v4/qv4objectproto.cpp b/src/qml/qml/v4/qv4objectproto.cpp deleted file mode 100644 index 462c9ca81e..0000000000 --- a/src/qml/qml/v4/qv4objectproto.cpp +++ /dev/null @@ -1,624 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - - -#include "qv4objectproto_p.h" -#include "qv4mm_p.h" -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#ifndef Q_OS_WIN -# include -# ifndef Q_OS_VXWORKS -# include -# else -# include "qplatformdefs.h" -# endif -#else -# include -#endif - -using namespace QV4; - - -DEFINE_MANAGED_VTABLE(ObjectCtor); - -ObjectCtor::ObjectCtor(ExecutionContext *scope) - : FunctionObject(scope, scope->engine->newIdentifier(QStringLiteral("Object"))) -{ - vtbl = &static_vtbl; -} - -Value ObjectCtor::construct(Managed *that, Value *args, int argc) -{ - ObjectCtor *ctor = static_cast(that); - ExecutionEngine *v4 = that->engine(); - if (!argc || args[0].isUndefined() || args[0].isNull()) { - Object *obj = v4->newObject(); - Value proto = ctor->get(v4->id_prototype); - if (proto.isObject()) - obj->prototype = proto.objectValue(); - return Value::fromObject(obj); - } - return __qmljs_to_object(v4->current, args[0]); -} - -Value ObjectCtor::call(Managed *m, const Value &/*thisObject*/, Value *args, int argc) -{ - if (!argc || args[0].isUndefined() || args[0].isNull()) - return Value::fromObject(m->engine()->newObject()); - return __qmljs_to_object(m->engine()->current, args[0]); -} - -void ObjectPrototype::init(ExecutionContext *ctx, const Value &ctor) -{ - ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); - ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); - ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("getPrototypeOf"), method_getPrototypeOf, 1); - ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("getOwnPropertyDescriptor"), method_getOwnPropertyDescriptor, 2); - ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("getOwnPropertyNames"), method_getOwnPropertyNames, 1); - ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("create"), method_create, 2); - ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("defineProperty"), method_defineProperty, 3); - ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("defineProperties"), method_defineProperties, 2); - ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("seal"), method_seal, 1); - ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("freeze"), method_freeze, 1); - ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("preventExtensions"), method_preventExtensions, 1); - ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("isSealed"), method_isSealed, 1); - ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("isFrozen"), method_isFrozen, 1); - ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("isExtensible"), method_isExtensible, 1); - ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("keys"), method_keys, 1); - - defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); - defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); - defineDefaultProperty(ctx, QStringLiteral("toLocaleString"), method_toLocaleString, 0); - defineDefaultProperty(ctx, QStringLiteral("valueOf"), method_valueOf, 0); - defineDefaultProperty(ctx, QStringLiteral("hasOwnProperty"), method_hasOwnProperty, 1); - defineDefaultProperty(ctx, QStringLiteral("isPrototypeOf"), method_isPrototypeOf, 1); - defineDefaultProperty(ctx, QStringLiteral("propertyIsEnumerable"), method_propertyIsEnumerable, 1); - defineDefaultProperty(ctx, QStringLiteral("__defineGetter__"), method_defineGetter, 2); - defineDefaultProperty(ctx, QStringLiteral("__defineSetter__"), method_defineSetter, 2); - - ExecutionEngine *v4 = ctx->engine; - Property *p = insertMember(v4->id___proto__, Attr_Accessor|Attr_NotEnumerable); - p->setGetter(v4->newBuiltinFunction(v4->rootContext, v4->id___proto__, method_get_proto)); - p->setSetter(v4->newBuiltinFunction(v4->rootContext, v4->id___proto__, method_set_proto)); -} - -Value ObjectPrototype::method_getPrototypeOf(SimpleCallContext *ctx) -{ - Value o = ctx->argument(0); - if (! o.isObject()) - ctx->throwTypeError(); - - Object *p = o.objectValue()->prototype; - return p ? Value::fromObject(p) : Value::nullValue(); -} - -Value ObjectPrototype::method_getOwnPropertyDescriptor(SimpleCallContext *ctx) -{ - Value O = ctx->argument(0); - if (!O.isObject()) - ctx->throwTypeError(); - - String *name = ctx->argument(1).toString(ctx); - PropertyAttributes attrs; - Property *desc = O.objectValue()->__getOwnProperty__(name, &attrs); - return fromPropertyDescriptor(ctx, desc, attrs); -} - -Value ObjectPrototype::method_getOwnPropertyNames(SimpleCallContext *context) -{ - Object *O = context->argumentCount ? context->arguments[0].asObject() : 0; - if (!O) - context->throwTypeError(); - - ArrayObject *array = getOwnPropertyNames(context->engine, context->arguments[0]); - return Value::fromObject(array); -} - -Value ObjectPrototype::method_create(SimpleCallContext *ctx) -{ - Value O = ctx->argument(0); - if (!O.isObject() && !O.isNull()) - ctx->throwTypeError(); - - Object *newObject = ctx->engine->newObject(); - newObject->prototype = O.asObject(); - - Value objValue = Value::fromObject(newObject); - if (ctx->argumentCount > 1 && !ctx->argument(1).isUndefined()) { - ctx->arguments[0] = objValue; - method_defineProperties(ctx); - } - - return objValue; -} - -Value ObjectPrototype::method_defineProperty(SimpleCallContext *ctx) -{ - Value O = ctx->argument(0); - if (!O.isObject()) - ctx->throwTypeError(); - - String *name = ctx->argument(1).toString(ctx); - - Value attributes = ctx->argument(2); - Property pd; - PropertyAttributes attrs; - toPropertyDescriptor(ctx, attributes, &pd, &attrs); - - if (!O.objectValue()->__defineOwnProperty__(ctx, name, pd, attrs)) - ctx->throwTypeError(); - - return O; -} - -Value ObjectPrototype::method_defineProperties(SimpleCallContext *ctx) -{ - Value O = ctx->argument(0); - if (!O.isObject()) - ctx->throwTypeError(); - - Object *o = ctx->argument(1).toObject(ctx); - - ObjectIterator it(o, ObjectIterator::EnumerableOnly); - while (1) { - uint index; - String *name; - PropertyAttributes attrs; - Property *pd = it.next(&name, &index, &attrs); - if (!pd) - break; - Property n; - PropertyAttributes nattrs; - toPropertyDescriptor(ctx, o->getValue(pd, attrs), &n, &nattrs); - bool ok; - if (name) - ok = O.objectValue()->__defineOwnProperty__(ctx, name, n, nattrs); - else - ok = O.objectValue()->__defineOwnProperty__(ctx, index, n, nattrs); - if (!ok) - ctx->throwTypeError(); - } - - return O; -} - -Value ObjectPrototype::method_seal(SimpleCallContext *ctx) -{ - if (!ctx->argument(0).isObject()) - ctx->throwTypeError(); - - Object *o = ctx->argument(0).objectValue(); - o->extensible = false; - - o->internalClass = o->internalClass->sealed(); - - o->ensureArrayAttributes(); - for (uint i = 0; i < o->arrayDataLen; ++i) { - if (!o->arrayAttributes[i].isGeneric()) - o->arrayAttributes[i].setConfigurable(false); - } - - return ctx->argument(0); -} - -Value ObjectPrototype::method_freeze(SimpleCallContext *ctx) -{ - if (!ctx->argument(0).isObject()) - ctx->throwTypeError(); - - Object *o = ctx->argument(0).objectValue(); - o->extensible = false; - - o->internalClass = o->internalClass->frozen(); - - o->ensureArrayAttributes(); - for (uint i = 0; i < o->arrayDataLen; ++i) { - if (!o->arrayAttributes[i].isGeneric()) - o->arrayAttributes[i].setConfigurable(false); - if (o->arrayAttributes[i].isData()) - o->arrayAttributes[i].setWritable(false); - } - return ctx->argument(0); -} - -Value ObjectPrototype::method_preventExtensions(SimpleCallContext *ctx) -{ - if (!ctx->argument(0).isObject()) - ctx->throwTypeError(); - - Object *o = ctx->argument(0).objectValue(); - o->extensible = false; - return ctx->argument(0); -} - -Value ObjectPrototype::method_isSealed(SimpleCallContext *ctx) -{ - if (!ctx->argument(0).isObject()) - ctx->throwTypeError(); - - Object *o = ctx->argument(0).objectValue(); - if (o->extensible) - return Value::fromBoolean(false); - - if (o->internalClass != o->internalClass->sealed()) - return Value::fromBoolean(false); - - if (!o->arrayDataLen) - return Value::fromBoolean(true); - - if (!o->arrayAttributes) - return Value::fromBoolean(false); - - for (uint i = 0; i < o->arrayDataLen; ++i) { - if (!o->arrayAttributes[i].isGeneric()) - if (o->arrayAttributes[i].isConfigurable()) - return Value::fromBoolean(false); - } - - return Value::fromBoolean(true); -} - -Value ObjectPrototype::method_isFrozen(SimpleCallContext *ctx) -{ - if (!ctx->argument(0).isObject()) - ctx->throwTypeError(); - - Object *o = ctx->argument(0).objectValue(); - if (o->extensible) - return Value::fromBoolean(false); - - if (o->internalClass != o->internalClass->frozen()) - return Value::fromBoolean(false); - - if (!o->arrayDataLen) - return Value::fromBoolean(true); - - if (!o->arrayAttributes) - return Value::fromBoolean(false); - - for (uint i = 0; i < o->arrayDataLen; ++i) { - if (!o->arrayAttributes[i].isGeneric()) - if (o->arrayAttributes[i].isConfigurable() || o->arrayAttributes[i].isWritable()) - return Value::fromBoolean(false); - } - - return Value::fromBoolean(true); -} - -Value ObjectPrototype::method_isExtensible(SimpleCallContext *ctx) -{ - if (!ctx->argument(0).isObject()) - ctx->throwTypeError(); - - Object *o = ctx->argument(0).objectValue(); - return Value::fromBoolean(o->extensible); -} - -Value ObjectPrototype::method_keys(SimpleCallContext *ctx) -{ - if (!ctx->argument(0).isObject()) - ctx->throwTypeError(); - - Object *o = ctx->argument(0).objectValue(); - - ArrayObject *a = ctx->engine->newArrayObject(); - - ObjectIterator it(o, ObjectIterator::EnumerableOnly); - while (1) { - Value name = it.nextPropertyNameAsString(); - if (name.isNull()) - break; - a->push_back(name); - } - - return Value::fromObject(a); -} - -Value ObjectPrototype::method_toString(SimpleCallContext *ctx) -{ - if (ctx->thisObject.isUndefined()) { - return Value::fromString(ctx, QStringLiteral("[object Undefined]")); - } else if (ctx->thisObject.isNull()) { - return Value::fromString(ctx, QStringLiteral("[object Null]")); - } else { - Value obj = __qmljs_to_object(ctx, ctx->thisObject); - QString className = obj.objectValue()->className(); - return Value::fromString(ctx, QString::fromUtf8("[object %1]").arg(className)); - } -} - -Value ObjectPrototype::method_toLocaleString(SimpleCallContext *ctx) -{ - Object *o = ctx->thisObject.toObject(ctx); - Value ts = o->get(ctx->engine->newString(QStringLiteral("toString"))); - FunctionObject *f = ts.asFunctionObject(); - if (!f) - ctx->throwTypeError(); - return f->call(Value::fromObject(o), 0, 0); -} - -Value ObjectPrototype::method_valueOf(SimpleCallContext *ctx) -{ - return Value::fromObject(ctx->thisObject.toObject(ctx)); -} - -Value ObjectPrototype::method_hasOwnProperty(SimpleCallContext *ctx) -{ - String *P = ctx->argument(0).toString(ctx); - Object *O = ctx->thisObject.toObject(ctx); - bool r = O->__getOwnProperty__(P) != 0; - return Value::fromBoolean(r); -} - -Value ObjectPrototype::method_isPrototypeOf(SimpleCallContext *ctx) -{ - Value V = ctx->argument(0); - if (! V.isObject()) - return Value::fromBoolean(false); - - Object *O = ctx->thisObject.toObject(ctx); - Object *proto = V.objectValue()->prototype; - while (proto) { - if (O == proto) - return Value::fromBoolean(true); - proto = proto->prototype; - } - return Value::fromBoolean(false); -} - -Value ObjectPrototype::method_propertyIsEnumerable(SimpleCallContext *ctx) -{ - String *p = ctx->argument(0).toString(ctx); - - Object *o = ctx->thisObject.toObject(ctx); - PropertyAttributes attrs; - o->__getOwnProperty__(p, &attrs); - return Value::fromBoolean(attrs.isEnumerable()); -} - -Value ObjectPrototype::method_defineGetter(SimpleCallContext *ctx) -{ - if (ctx->argumentCount < 2) - ctx->throwTypeError(); - String *prop = ctx->argument(0).toString(ctx); - - FunctionObject *f = ctx->argument(1).asFunctionObject(); - if (!f) - ctx->throwTypeError(); - - Object *o = ctx->thisObject.asObject(); - if (!o) { - if (!ctx->thisObject.isUndefined()) - return Value::undefinedValue(); - o = ctx->engine->globalObject; - } - - Property pd = Property::fromAccessor(f, 0); - o->__defineOwnProperty__(ctx, prop, pd, Attr_Accessor); - return Value::undefinedValue(); -} - -Value ObjectPrototype::method_defineSetter(SimpleCallContext *ctx) -{ - if (ctx->argumentCount < 2) - ctx->throwTypeError(); - String *prop = ctx->argument(0).toString(ctx); - - FunctionObject *f = ctx->argument(1).asFunctionObject(); - if (!f) - ctx->throwTypeError(); - - Object *o = ctx->thisObject.asObject(); - if (!o) { - if (!ctx->thisObject.isUndefined()) - return Value::undefinedValue(); - o = ctx->engine->globalObject; - } - - Property pd = Property::fromAccessor(0, f); - o->__defineOwnProperty__(ctx, prop, pd, Attr_Accessor); - return Value::undefinedValue(); -} - -Value ObjectPrototype::method_get_proto(SimpleCallContext *ctx) -{ - Object *o = ctx->thisObject.asObject(); - if (!o) - ctx->throwTypeError(); - - return Value::fromObject(o->prototype); -} - -Value ObjectPrototype::method_set_proto(SimpleCallContext *ctx) -{ - Object *o = ctx->thisObject.asObject(); - if (!o) - ctx->throwTypeError(); - - Value proto = ctx->argument(0); - bool ok = false; - if (proto.isNull()) { - o->prototype = 0; - ok = true; - } else if (Object *p = proto.asObject()) { - if (o->prototype == p) { - ok = true; - } else if (o->extensible) { - Object *pp = p; - while (pp) { - if (pp == o) - break; - pp = pp->prototype; - } - if (!pp) { - ok = true; - o->prototype = p; - } - } - } - if (!ok) - ctx->throwTypeError(QStringLiteral("Cyclic __proto__ value")); - return Value::undefinedValue(); -} - -void ObjectPrototype::toPropertyDescriptor(ExecutionContext *ctx, Value v, Property *desc, PropertyAttributes *attrs) -{ - if (!v.isObject()) - ctx->throwTypeError(); - - Object *o = v.objectValue(); - - attrs->clear(); - desc->setGetter(0); - desc->setSetter(0); - - if (o->__hasProperty__(ctx->engine->id_enumerable)) - attrs->setEnumerable(o->get(ctx->engine->id_enumerable).toBoolean()); - - if (o->__hasProperty__(ctx->engine->id_configurable)) - attrs->setConfigurable(o->get(ctx->engine->id_configurable).toBoolean()); - - if (o->__hasProperty__(ctx->engine->id_get)) { - Value get = o->get(ctx->engine->id_get); - FunctionObject *f = get.asFunctionObject(); - if (f) { - desc->setGetter(f); - } else if (get.isUndefined()) { - desc->setGetter((FunctionObject *)0x1); - } else { - ctx->throwTypeError(); - } - attrs->setType(PropertyAttributes::Accessor); - } - - if (o->__hasProperty__(ctx->engine->id_set)) { - Value set = o->get(ctx->engine->id_set); - FunctionObject *f = set.asFunctionObject(); - if (f) { - desc->setSetter(f); - } else if (set.isUndefined()) { - desc->setSetter((FunctionObject *)0x1); - } else { - ctx->throwTypeError(); - } - attrs->setType(PropertyAttributes::Accessor); - } - - if (o->__hasProperty__(ctx->engine->id_writable)) { - if (attrs->isAccessor()) - ctx->throwTypeError(); - attrs->setWritable(o->get(ctx->engine->id_writable).toBoolean()); - // writable forces it to be a data descriptor - desc->value = Value::undefinedValue(); - } - - if (o->__hasProperty__(ctx->engine->id_value)) { - if (attrs->isAccessor()) - ctx->throwTypeError(); - desc->value = o->get(ctx->engine->id_value); - attrs->setType(PropertyAttributes::Data); - } - - if (attrs->isGeneric()) - desc->value = Value::emptyValue(); -} - - -Value ObjectPrototype::fromPropertyDescriptor(ExecutionContext *ctx, const Property *desc, PropertyAttributes attrs) -{ - if (!desc) - return Value::undefinedValue(); - - ExecutionEngine *engine = ctx->engine; -// Let obj be the result of creating a new object as if by the expression new Object() where Object is the standard built-in constructor with that name. - Object *o = engine->newObject(); - - Property pd; - if (attrs.isData()) { - pd.value = desc->value; - o->__defineOwnProperty__(ctx, engine->newString(QStringLiteral("value")), pd, Attr_Data); - pd.value = Value::fromBoolean(attrs.isWritable()); - o->__defineOwnProperty__(ctx, engine->newString(QStringLiteral("writable")), pd, Attr_Data); - } else { - pd.value = desc->getter() ? Value::fromObject(desc->getter()) : Value::undefinedValue(); - o->__defineOwnProperty__(ctx, engine->newString(QStringLiteral("get")), pd, Attr_Data); - pd.value = desc->setter() ? Value::fromObject(desc->setter()) : Value::undefinedValue(); - o->__defineOwnProperty__(ctx, engine->newString(QStringLiteral("set")), pd, Attr_Data); - } - pd.value = Value::fromBoolean(attrs.isEnumerable()); - o->__defineOwnProperty__(ctx, engine->newString(QStringLiteral("enumerable")), pd, Attr_Data); - pd.value = Value::fromBoolean(attrs.isConfigurable()); - o->__defineOwnProperty__(ctx, engine->newString(QStringLiteral("configurable")), pd, Attr_Data); - - return Value::fromObject(o); -} - - -ArrayObject *ObjectPrototype::getOwnPropertyNames(ExecutionEngine *v4, const Value &o) -{ - ArrayObject *array = v4->newArrayObject(); - Object *O = o.asObject(); - if (!O) - return array; - - ObjectIterator it(O, ObjectIterator::NoFlags); - while (1) { - Value name = it.nextPropertyNameAsString(); - if (name.isNull()) - break; - array->push_back(name); - } - return array; -} diff --git a/src/qml/qml/v4/qv4objectproto_p.h b/src/qml/qml/v4/qv4objectproto_p.h deleted file mode 100644 index ca2e77ca42..0000000000 --- a/src/qml/qml/v4/qv4objectproto_p.h +++ /dev/null @@ -1,107 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QV4ECMAOBJECTS_P_H -#define QV4ECMAOBJECTS_P_H - -#include "qv4object_p.h" -#include "qv4functionobject_p.h" -#include - -QT_BEGIN_NAMESPACE - -namespace QV4 { - -struct ObjectCtor: FunctionObject -{ - ObjectCtor(ExecutionContext *scope); - - static Value construct(Managed *that, Value *args, int argc); - static Value call(Managed *that, const Value &, Value *, int); - -protected: - static const ManagedVTable static_vtbl; -}; - -struct ObjectPrototype: Object -{ - ObjectPrototype(ExecutionEngine *engine) : Object(engine) {} - - void init(ExecutionContext *ctx, const Value &ctor); - - static Value method_getPrototypeOf(SimpleCallContext *ctx); - static Value method_getOwnPropertyDescriptor(SimpleCallContext *ctx); - static Value method_getOwnPropertyNames(SimpleCallContext *context); - static Value method_create(SimpleCallContext *ctx); - static Value method_defineProperty(SimpleCallContext *ctx); - static Value method_defineProperties(SimpleCallContext *ctx); - static Value method_seal(SimpleCallContext *ctx); - static Value method_freeze(SimpleCallContext *ctx); - static Value method_preventExtensions(SimpleCallContext *ctx); - static Value method_isSealed(SimpleCallContext *ctx); - static Value method_isFrozen(SimpleCallContext *ctx); - static Value method_isExtensible(SimpleCallContext *ctx); - static Value method_keys(SimpleCallContext *ctx); - - static Value method_toString(SimpleCallContext *ctx); - static Value method_toLocaleString(SimpleCallContext *ctx); - static Value method_valueOf(SimpleCallContext *ctx); - static Value method_hasOwnProperty(SimpleCallContext *ctx); - static Value method_isPrototypeOf(SimpleCallContext *ctx); - static Value method_propertyIsEnumerable(SimpleCallContext *ctx); - - static Value method_defineGetter(SimpleCallContext *ctx); - static Value method_defineSetter(SimpleCallContext *ctx); - - static Value method_get_proto(SimpleCallContext *ctx); - static Value method_set_proto(SimpleCallContext *ctx); - - static void toPropertyDescriptor(ExecutionContext *ctx, Value v, Property *desc, PropertyAttributes *attrs); - static Value fromPropertyDescriptor(ExecutionContext *ctx, const Property *desc, PropertyAttributes attrs); - - static ArrayObject *getOwnPropertyNames(ExecutionEngine *v4, const Value &o); -}; - - -} - -QT_END_NAMESPACE - -#endif // QV4ECMAOBJECTS_P_H diff --git a/src/qml/qml/v4/qv4property_p.h b/src/qml/qml/v4/qv4property_p.h deleted file mode 100644 index 024ad3c720..0000000000 --- a/src/qml/qml/v4/qv4property_p.h +++ /dev/null @@ -1,150 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QV4PROPERTYDESCRIPTOR_H -#define QV4PROPERTYDESCRIPTOR_H - -#include "qv4global_p.h" -#include "qv4value_p.h" -#include "qv4internalclass_p.h" - -QT_BEGIN_NAMESPACE - -namespace QV4 { - -struct FunctionObject; - -struct Property { - union { - Value value; - struct { - FunctionObject *get; - FunctionObject *set; - }; - }; - - // Section 8.10 - inline void fullyPopulated(PropertyAttributes *attrs) { - if (!attrs->hasType()) { - value = Value::undefinedValue(); - } - if (attrs->type() == PropertyAttributes::Accessor) { - attrs->clearWritable(); - if (get == (FunctionObject *)0x1) - get = 0; - if (set == (FunctionObject *)0x1) - set = 0; - } - attrs->resolve(); - } - - static inline Property fromValue(Value v) { - Property pd; - pd.value = v; - return pd; - } - static inline Property fromAccessor(FunctionObject *getter, FunctionObject *setter) { - Property pd; - pd.get = getter; - pd.set = setter; - return pd; - } - - static Property genericDescriptor() { - Property pd; - pd.value = Value::emptyValue(); - return pd; - } - - inline bool isSubset(const PropertyAttributes &attrs, const Property &other, PropertyAttributes otherAttrs) const; - inline void merge(PropertyAttributes &attrs, const Property &other, PropertyAttributes otherAttrs); - - inline FunctionObject *getter() const { return get; } - inline FunctionObject *setter() const { return set; } - inline void setGetter(FunctionObject *g) { get = g; } - inline void setSetter(FunctionObject *s) { set = s; } -}; - -inline bool Property::isSubset(const PropertyAttributes &attrs, const Property &other, PropertyAttributes otherAttrs) const -{ - if (attrs.type() != PropertyAttributes::Generic && attrs.type() != otherAttrs.type()) - return false; - if (attrs.hasEnumerable() && attrs.isEnumerable() != otherAttrs.isEnumerable()) - return false; - if (attrs.hasConfigurable() && attrs.isConfigurable() != otherAttrs.isConfigurable()) - return false; - if (attrs.hasWritable() && attrs.isWritable() != otherAttrs.isWritable()) - return false; - if (attrs.type() == PropertyAttributes::Data && !value.sameValue(other.value)) - return false; - if (attrs.type() == PropertyAttributes::Accessor) { - if (get != other.get) - return false; - if (set != other.set) - return false; - } - return true; -} - -inline void Property::merge(PropertyAttributes &attrs, const Property &other, PropertyAttributes otherAttrs) -{ - if (otherAttrs.hasEnumerable()) - attrs.setEnumerable(otherAttrs.isEnumerable()); - if (otherAttrs.hasConfigurable()) - attrs.setConfigurable(otherAttrs.isConfigurable()); - if (otherAttrs.hasWritable()) - attrs.setWritable(otherAttrs.isWritable()); - if (otherAttrs.type() == PropertyAttributes::Accessor) { - attrs.setType(PropertyAttributes::Accessor); - if (other.get) - get = (other.get == (FunctionObject *)0x1) ? 0 : other.get; - if (other.set) - set = (other.set == (FunctionObject *)0x1) ? 0 : other.set; - } else if (otherAttrs.type() == PropertyAttributes::Data){ - attrs.setType(PropertyAttributes::Data); - value = other.value; - } -} - -} - -QT_END_NAMESPACE - -#endif diff --git a/src/qml/qml/v4/qv4qmlextensions.cpp b/src/qml/qml/v4/qv4qmlextensions.cpp deleted file mode 100644 index a55330ff67..0000000000 --- a/src/qml/qml/v4/qv4qmlextensions.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4qmlextensions_p.h" -#include "qv4object_p.h" - -using namespace QV4; - -void QmlExtensions::markObjects() -{ - if (valueTypeWrapperPrototype) - valueTypeWrapperPrototype->mark(); -} diff --git a/src/qml/qml/v4/qv4qmlextensions_p.h b/src/qml/qml/v4/qv4qmlextensions_p.h deleted file mode 100644 index cf9e287efe..0000000000 --- a/src/qml/qml/v4/qv4qmlextensions_p.h +++ /dev/null @@ -1,66 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QV4QMLEXTENSIONS_P_H -#define QV4QMLEXTENSIONS_P_H - -#include - -QT_BEGIN_NAMESPACE - -namespace QV4 { -struct Object; - -struct Q_QML_EXPORT QmlExtensions -{ - QmlExtensions() - : valueTypeWrapperPrototype(0) - {} - - Object *valueTypeWrapperPrototype; - - void markObjects(); -}; - -} // namespace QV4 - -QT_END_NAMESPACE - -#endif diff --git a/src/qml/qml/v4/qv4qobjectwrapper.cpp b/src/qml/qml/v4/qv4qobjectwrapper.cpp deleted file mode 100644 index f79675845b..0000000000 --- a/src/qml/qml/v4/qv4qobjectwrapper.cpp +++ /dev/null @@ -1,1792 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4qobjectwrapper_p.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE - -#if defined(__GNUC__) && !defined(__INTEL_COMPILER) -# if (__GNUC__ * 100 + __GNUC_MINOR__) >= 405 -// The code in this file does not violate strict aliasing, but GCC thinks it does -// so turn off the warnings for us to have a clean build -# pragma GCC diagnostic ignored "-Wstrict-aliasing" -# endif -#endif - - -using namespace QV4; - -static QPair extractQtMethod(QV4::FunctionObject *function) -{ - if (function && function->subtype == QV4::FunctionObject::WrappedQtMethod) { - QObjectMethod *method = static_cast(function); - return qMakePair(method->object(), method->methodIndex()); - } - - return qMakePair((QObject *)0, -1); -} - -static QPair extractQtSignal(const Value &value) -{ - if (QV4::FunctionObject *function = value.asFunctionObject()) - return extractQtMethod(function); - - if (QV4::QmlSignalHandler *handler = value.as()) - return qMakePair(handler->object(), handler->signalIndex()); - - return qMakePair((QObject *)0, -1); -} - - -struct ReadAccessor { - static inline void Indirect(QObject *object, const QQmlPropertyData &property, - void *output, QQmlNotifier **n) - { - Q_ASSERT(n == 0); - Q_UNUSED(n); - - void *args[] = { output, 0 }; - QMetaObject::metacall(object, QMetaObject::ReadProperty, property.coreIndex, args); - } - - static inline void Direct(QObject *object, const QQmlPropertyData &property, - void *output, QQmlNotifier **n) - { - Q_ASSERT(n == 0); - Q_UNUSED(n); - - void *args[] = { output, 0 }; - object->qt_metacall(QMetaObject::ReadProperty, property.coreIndex, args); - } - - static inline void Accessor(QObject *object, const QQmlPropertyData &property, - void *output, QQmlNotifier **n) - { - Q_ASSERT(property.accessors); - - property.accessors->read(object, property.accessorData, output); - if (n) property.accessors->notifier(object, property.accessorData, n); - } -}; - -static inline QV4::Value valueToHandle(QV4::ExecutionEngine *, int v) -{ return QV4::Value::fromInt32(v); } -static inline QV4::Value valueToHandle(QV4::ExecutionEngine *, uint v) -{ return QV4::Value::fromUInt32(v); } -static inline QV4::Value valueToHandle(QV4::ExecutionEngine *, bool v) -{ return QV4::Value::fromBoolean(v); } -static inline QV4::Value valueToHandle(QV4::ExecutionEngine *e, const QString &v) -{ return QV4::Value::fromString(e, v); } -static inline QV4::Value valueToHandle(QV4::ExecutionEngine *, float v) -{ return QV4::Value::fromDouble(v); } -static inline QV4::Value valueToHandle(QV4::ExecutionEngine *, double v) -{ return QV4::Value::fromDouble(v); } -static inline QV4::Value valueToHandle(QV4::ExecutionEngine *e, QObject *v) -{ return QV4::QObjectWrapper::wrap(e, v); } - -// Load value properties -template -static QV4::Value LoadProperty(QV8Engine *engine, QObject *object, - const QQmlPropertyData &property, - QQmlNotifier **notifier) -{ - Q_ASSERT(!property.isFunction()); - QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine); - - if (property.isQObject()) { - QObject *rv = 0; - ReadFunction(object, property, &rv, notifier); - return QV4::QObjectWrapper::wrap(v4, rv); - } else if (property.isQList()) { - return QmlListWrapper::create(engine, object, property.coreIndex, property.propType); - } else if (property.propType == QMetaType::QReal) { - qreal v = 0; - ReadFunction(object, property, &v, notifier); - return valueToHandle(v4, v); - } else if (property.propType == QMetaType::Int || property.isEnum()) { - int v = 0; - ReadFunction(object, property, &v, notifier); - return valueToHandle(v4, v); - } else if (property.propType == QMetaType::Bool) { - bool v = false; - ReadFunction(object, property, &v, notifier); - return valueToHandle(v4, v); - } else if (property.propType == QMetaType::QString) { - QString v; - ReadFunction(object, property, &v, notifier); - return valueToHandle(v4, v); - } else if (property.propType == QMetaType::UInt) { - uint v = 0; - ReadFunction(object, property, &v, notifier); - return valueToHandle(v4, v); - } else if (property.propType == QMetaType::Float) { - float v = 0; - ReadFunction(object, property, &v, notifier); - return valueToHandle(v4, v); - } else if (property.propType == QMetaType::Double) { - double v = 0; - ReadFunction(object, property, &v, notifier); - return valueToHandle(v4, v); - } else if (property.isV4Handle()) { - QQmlV4Handle handle; - ReadFunction(object, property, &handle, notifier); - return handle.toValue(); - } else if (property.propType == qMetaTypeId()) { - QJSValue v; - ReadFunction(object, property, &v, notifier); - return QJSValuePrivate::get(v)->getValue(v4); - } else if (property.isQVariant()) { - QVariant v; - ReadFunction(object, property, &v, notifier); - - if (QQmlValueTypeFactory::isValueType(v.userType())) { - if (QQmlValueType *valueType = QQmlValueTypeFactory::valueType(v.userType())) - return QV4::QmlValueTypeWrapper::create(engine, object, property.coreIndex, valueType); // VariantReference value-type. - } - - return engine->fromVariant(v); - } else if (QQmlValueTypeFactory::isValueType(property.propType)) { - Q_ASSERT(notifier == 0); - - if (QQmlValueType *valueType = QQmlValueTypeFactory::valueType(property.propType)) - return QV4::QmlValueTypeWrapper::create(engine, object, property.coreIndex, valueType); - } else { - Q_ASSERT(notifier == 0); - - // see if it's a sequence type - bool succeeded = false; - QV4::Value retn = QV4::SequencePrototype::newSequence(v4, property.propType, object, property.coreIndex, &succeeded); - if (succeeded) - return retn; - } - - if (property.propType == QMetaType::UnknownType) { - QMetaProperty p = object->metaObject()->property(property.coreIndex); - qWarning("QMetaProperty::read: Unable to handle unregistered datatype '%s' for property " - "'%s::%s'", p.typeName(), object->metaObject()->className(), p.name()); - return QV4::Value::undefinedValue(); - } else { - QVariant v(property.propType, (void *)0); - ReadFunction(object, property, v.data(), notifier); - return engine->fromVariant(v); - } -} - -QObjectWrapper::QObjectWrapper(ExecutionEngine *engine, QObject *object) - : Object(engine) - , m_object(object) -{ - vtbl = &static_vtbl; - prototype = engine->objectPrototype; - - m_destroy = engine->newIdentifier(QStringLiteral("destroy")); - m_toString = engine->newIdentifier(QStringLiteral("toString")); -} - -void QObjectWrapper::initializeBindings(ExecutionEngine *engine) -{ - engine->functionPrototype->defineDefaultProperty(engine, QStringLiteral("connect"), method_connect); - engine->functionPrototype->defineDefaultProperty(engine, QStringLiteral("disconnect"), method_disconnect); -} - -QQmlPropertyData *QObjectWrapper::findProperty(ExecutionEngine *engine, QQmlContextData *qmlContext, String *name, RevisionMode revisionMode, QQmlPropertyData *local) const -{ - QQmlData *ddata = QQmlData::get(m_object, false); - if (!ddata) - return 0; - QQmlPropertyData *result = 0; - if (ddata && ddata->propertyCache) - result = ddata->propertyCache->property(name, m_object, qmlContext); - if (!result) - result = QQmlPropertyCache::property(engine->v8Engine->engine(), m_object, name, qmlContext, *local); - return result; -} - -Value QObjectWrapper::getQmlProperty(ExecutionContext *ctx, QQmlContextData *qmlContext, String *name, QObjectWrapper::RevisionMode revisionMode, bool *hasProperty, bool includeImports) -{ - if (QQmlData::wasDeleted(m_object)) { - if (hasProperty) - *hasProperty = false; - return QV4::Value::undefinedValue(); - } - - if (name->isEqualTo(m_destroy) || name->isEqualTo(m_toString)) { - int index = name->isEqualTo(m_destroy) ? QV4::QObjectMethod::DestroyMethod : QV4::QObjectMethod::ToStringMethod; - QV4::Value method = QV4::QObjectMethod::create(ctx->engine->rootContext, m_object, index); - if (hasProperty) - *hasProperty = true; - return method; - } - - QQmlPropertyData local; - QQmlPropertyData *result = findProperty(ctx->engine, qmlContext, name, revisionMode, &local); - - if (!result) { - if (includeImports && name->startsWithUpper()) { - // Check for attached properties - if (qmlContext && qmlContext->imports) { - QQmlTypeNameCache::Result r = qmlContext->imports->query(name); - - if (hasProperty) - *hasProperty = true; - - if (r.isValid()) { - if (r.scriptIndex != -1) { - return QV4::Value::undefinedValue(); - } else if (r.type) { - return QmlTypeWrapper::create(ctx->engine->v8Engine, m_object, r.type, QmlTypeWrapper::ExcludeEnums); - } else if (r.importNamespace) { - return QmlTypeWrapper::create(ctx->engine->v8Engine, m_object, qmlContext->imports, r.importNamespace, QmlTypeWrapper::ExcludeEnums); - } - Q_ASSERT(!"Unreachable"); - } - } - } - return QV4::Object::get(this, name, hasProperty); - } - - QQmlData::flushPendingBinding(m_object, result->coreIndex); - QQmlData *ddata = QQmlData::get(m_object, false); - - if (revisionMode == QV4::QObjectWrapper::CheckRevision && result->hasRevision()) { - if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result)) { - if (hasProperty) - *hasProperty = false; - return QV4::Value::undefinedValue(); - } - } - - if (hasProperty) - *hasProperty = true; - - if (result->isFunction() && !result->isVarProperty()) { - if (result->isVMEFunction()) { - QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(m_object); - Q_ASSERT(vmemo); - return vmemo->vmeMethod(result->coreIndex); - } else if (result->isV4Function()) { - return QV4::QObjectMethod::create(ctx->engine->rootContext, m_object, result->coreIndex, QV4::Value::fromObject(ctx->engine->qmlContextObject())); - } else if (result->isSignalHandler()) { - QV4::QmlSignalHandler *handler = new (ctx->engine->memoryManager) QV4::QmlSignalHandler(ctx->engine, m_object, result->coreIndex); - - QV4::String *connect = ctx->engine->newIdentifier(QStringLiteral("connect")); - QV4::String *disconnect = ctx->engine->newIdentifier(QStringLiteral("disconnect")); - handler->put(connect, ctx->engine->functionPrototype->get(connect)); - handler->put(disconnect, ctx->engine->functionPrototype->get(disconnect)); - - return QV4::Value::fromObject(handler); - } else { - return QV4::QObjectMethod::create(ctx->engine->rootContext, m_object, result->coreIndex); - } - } - - QQmlEnginePrivate *ep = ctx->engine->v8Engine->engine() ? QQmlEnginePrivate::get(ctx->engine->v8Engine->engine()) : 0; - - if (result->hasAccessors()) { - QQmlNotifier *n = 0; - QQmlNotifier **nptr = 0; - - if (ep && ep->propertyCapture && result->accessors->notifier) - nptr = &n; - - QV4::Value rv = LoadProperty(ctx->engine->v8Engine, m_object, *result, nptr); - - if (result->accessors->notifier) { - if (n) ep->captureProperty(n); - } else { - ep->captureProperty(m_object, result->coreIndex, result->notifyIndex); - } - - return rv; - } - - if (ep && !result->isConstant()) - ep->captureProperty(m_object, result->coreIndex, result->notifyIndex); - - if (result->isVarProperty()) { - QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(m_object); - Q_ASSERT(vmemo); - return vmemo->vmeProperty(result->coreIndex); - } else if (result->isDirect()) { - return LoadProperty(ctx->engine->v8Engine, m_object, *result, 0); - } else { - return LoadProperty(ctx->engine->v8Engine, m_object, *result, 0); - } -} - -Value QObjectWrapper::getQmlProperty(ExecutionContext *ctx, QQmlContextData *qmlContext, QObject *object, String *name, QObjectWrapper::RevisionMode revisionMode, bool *hasProperty) -{ - if (QQmlData::wasDeleted(object)) { - if (hasProperty) - *hasProperty = false; - return QV4::Value::nullValue(); - } - - if (!QQmlData::get(object, true)) { - if (hasProperty) - *hasProperty = false; - return QV4::Value::nullValue(); - } - - QObjectWrapper *wrapper = wrap(ctx->engine, object).as(); - if (!wrapper) { - if (hasProperty) - *hasProperty = false; - return QV4::Value::nullValue(); - } - return wrapper->getQmlProperty(ctx, qmlContext, name, revisionMode, hasProperty); -} - -bool QObjectWrapper::setQmlProperty(ExecutionContext *ctx, QQmlContextData *qmlContext, QObject *object, String *name, QObjectWrapper::RevisionMode revisionMode, const Value &value) -{ - if (QQmlData::wasDeleted(object)) - return false; - - QQmlPropertyData local; - QQmlPropertyData *result = 0; - { - result = QQmlPropertyCache::property(ctx->engine->v8Engine->engine(), object, name, qmlContext, local); - } - - if (!result) - return false; - - if (revisionMode == QV4::QObjectWrapper::CheckRevision && result->hasRevision()) { - QQmlData *ddata = QQmlData::get(object); - if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result)) - return false; - } - - if (!result->isWritable() && !result->isQList()) { - QString error = QLatin1String("Cannot assign to read-only property \"") + - name->toQString() + QLatin1Char('\"'); - ctx->throwTypeError(error); - } - - QQmlBinding *newBinding = 0; - if (FunctionObject *f = value.asFunctionObject()) { - if (!f->bindingKeyFlag) { - if (!result->isVarProperty() && result->propType != qMetaTypeId()) { - // assigning a JS function to a non var or QJSValue property or is not allowed. - QString error = QLatin1String("Cannot assign JavaScript function to "); - if (!QMetaType::typeName(result->propType)) - error += QLatin1String("[unknown property type]"); - else - error += QLatin1String(QMetaType::typeName(result->propType)); - ctx->throwError(error); - } - } else { - // binding assignment. - QQmlContextData *callingQmlContext = QV4::QmlContextWrapper::callingContext(ctx->engine); - - QV4::ExecutionEngine::StackFrame frame = ctx->engine->currentStackFrame(); - - newBinding = new QQmlBinding(value, object, callingQmlContext, frame.source, - qmlSourceCoordinate(frame.line), qmlSourceCoordinate(frame.column)); - newBinding->setTarget(object, *result, callingQmlContext); - newBinding->setEvaluateFlags(newBinding->evaluateFlags() | - QQmlBinding::RequiresThisObject); - } - } - - QQmlAbstractBinding *oldBinding = - QQmlPropertyPrivate::setBinding(object, result->coreIndex, -1, newBinding); - if (oldBinding) - oldBinding->destroy(); - - if (!newBinding && result->isVarProperty()) { - // allow assignment of "special" values (null, undefined, function) to var properties - QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object); - Q_ASSERT(vmemo); - vmemo->setVMEProperty(result->coreIndex, value); - return true; - } - -#define PROPERTY_STORE(cpptype, value) \ - cpptype o = value; \ - int status = -1; \ - int flags = 0; \ - void *argv[] = { &o, 0, &status, &flags }; \ - QMetaObject::metacall(object, QMetaObject::WriteProperty, result->coreIndex, argv); - - if (value.isNull() && result->isQObject()) { - PROPERTY_STORE(QObject*, 0); - } else if (value.isUndefined() && result->isResettable()) { - void *a[] = { 0 }; - QMetaObject::metacall(object, QMetaObject::ResetProperty, result->coreIndex, a); - } else if (value.isUndefined() && result->propType == qMetaTypeId()) { - PROPERTY_STORE(QVariant, QVariant()); - } else if (value.isUndefined() && result->propType == QMetaType::QJsonValue) { - PROPERTY_STORE(QJsonValue, QJsonValue(QJsonValue::Undefined)); - } else if (!newBinding && result->propType == qMetaTypeId()) { - PROPERTY_STORE(QJSValue, new QJSValuePrivate(ctx->engine, value)); - } else if (value.isUndefined()) { - QString error = QLatin1String("Cannot assign [undefined] to "); - if (!QMetaType::typeName(result->propType)) - error += QLatin1String("[unknown property type]"); - else - error += QLatin1String(QMetaType::typeName(result->propType)); - ctx->throwError(error); - } else if (value.asFunctionObject()) { - // this is handled by the binding creation above - } else if (result->propType == QMetaType::Int && value.isNumber()) { - PROPERTY_STORE(int, qRound(value.asDouble())); - } else if (result->propType == QMetaType::QReal && value.isNumber()) { - PROPERTY_STORE(qreal, qreal(value.asDouble())); - } else if (result->propType == QMetaType::Float && value.isNumber()) { - PROPERTY_STORE(float, float(value.asDouble())); - } else if (result->propType == QMetaType::Double && value.isNumber()) { - PROPERTY_STORE(double, double(value.asDouble())); - } else if (result->propType == QMetaType::QString && value.isString()) { - PROPERTY_STORE(QString, value.toQString()); - } else if (result->isVarProperty()) { - QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object); - Q_ASSERT(vmemo); - vmemo->setVMEProperty(result->coreIndex, value); - } else { - QVariant v; - if (result->isQList()) - v = ctx->engine->v8Engine->toVariant(value, qMetaTypeId >()); - else - v = ctx->engine->v8Engine->toVariant(value, result->propType); - - QQmlContextData *callingQmlContext = QV4::QmlContextWrapper::callingContext(ctx->engine); - if (!QQmlPropertyPrivate::write(object, *result, v, callingQmlContext)) { - const char *valueType = 0; - if (v.userType() == QVariant::Invalid) valueType = "null"; - else valueType = QMetaType::typeName(v.userType()); - - const char *targetTypeName = QMetaType::typeName(result->propType); - if (!targetTypeName) - targetTypeName = "an unregistered type"; - - QString error = QLatin1String("Cannot assign ") + - QLatin1String(valueType) + - QLatin1String(" to ") + - QLatin1String(targetTypeName); - ctx->throwError(error); - } - } - - return true; -} - -Value QObjectWrapper::wrap(ExecutionEngine *engine, QObject *object) -{ - if (QQmlData::wasDeleted(object)) - return QV4::Value::nullValue(); - - QQmlData *ddata = QQmlData::get(object, true); - if (!ddata) - return QV4::Value::undefinedValue(); - - if (ddata->jsEngineId == engine->m_engineId && !ddata->jsWrapper.isEmpty()) { - // We own the JS object - return ddata->jsWrapper.value(); - } else if (ddata->jsWrapper.isEmpty() && - (ddata->jsEngineId == engine->m_engineId || // We own the QObject - ddata->jsEngineId == 0 || // No one owns the QObject - !ddata->hasTaintedV8Object)) { // Someone else has used the QObject, but it isn't tainted - - QV4::Value rv = create(engine, ddata, object); - ddata->jsWrapper = rv; - ddata->jsEngineId = engine->m_engineId; - return rv; - - } else { - // If this object is tainted, we have to check to see if it is in our - // tainted object list - Object *alternateWrapper = 0; - if (engine->m_multiplyWrappedQObjects && ddata->hasTaintedV8Object) - alternateWrapper = engine->m_multiplyWrappedQObjects->value(object); - - // If our tainted handle doesn't exist or has been collected, and there isn't - // a handle in the ddata, we can assume ownership of the ddata->v8object - if (ddata->jsWrapper.isEmpty() && !alternateWrapper) { - QV4::Value result = create(engine, ddata, object); - ddata->jsWrapper = result; - ddata->jsEngineId = engine->m_engineId; - return result; - } - - if (!alternateWrapper) { - alternateWrapper = create(engine, ddata, object).asObject(); - if (!engine->m_multiplyWrappedQObjects) - engine->m_multiplyWrappedQObjects = new MultiplyWrappedQObjectMap; - engine->m_multiplyWrappedQObjects->insert(object, alternateWrapper); - ddata->hasTaintedV8Object = true; - } - - return QV4::Value::fromObject(alternateWrapper); - } -} - -QV4::Value QObjectWrapper::create(ExecutionEngine *engine, QQmlData *ddata, QObject *object) -{ - QQmlEngine *qmlEngine = engine->v8Engine->engine(); - if (!ddata->propertyCache && qmlEngine) { - ddata->propertyCache = QQmlEnginePrivate::get(qmlEngine)->cache(object); - if (ddata->propertyCache) ddata->propertyCache->addref(); - } - - return Value::fromObject(new (engine->memoryManager) QV4::QObjectWrapper(engine, object)); -} - -QV4::Value QObjectWrapper::get(Managed *m, String *name, bool *hasProperty) -{ - QObjectWrapper *that = static_cast(m); - ExecutionEngine *v4 = m->engine(); - QQmlContextData *qmlContext = QV4::QmlContextWrapper::callingContext(v4); - return that->getQmlProperty(v4->current, qmlContext, name, IgnoreRevision, hasProperty, /*includeImports*/ true); -} - -void QObjectWrapper::put(Managed *m, String *name, const Value &value) -{ - QObjectWrapper *that = static_cast(m); - ExecutionEngine *v4 = m->engine(); - - if (QQmlData::wasDeleted(that->m_object)) - return; - - QQmlContextData *qmlContext = QV4::QmlContextWrapper::callingContext(v4); - if (!setQmlProperty(v4->current, qmlContext, that->m_object, name, QV4::QObjectWrapper::IgnoreRevision, value)) { - QString error = QLatin1String("Cannot assign to non-existent property \"") + - name->toQString() + QLatin1Char('\"'); - v4->current->throwError(error); - } -} - -PropertyAttributes QObjectWrapper::query(const Managed *m, String *name) -{ - const QObjectWrapper *that = static_cast(m); - ExecutionEngine *engine = that->engine(); - QQmlContextData *qmlContext = QV4::QmlContextWrapper::callingContext(engine); - QQmlPropertyData local; - if (that->findProperty(engine, qmlContext, name, IgnoreRevision, &local) - || name->isEqualTo(that->m_destroy) || name->isEqualTo(that->m_toString)) - return QV4::Attr_Data; - else - return QV4::Object::query(m, name); -} - -Property *QObjectWrapper::advanceIterator(Managed *m, ObjectIterator *it, String **name, uint *index, PropertyAttributes *attributes) -{ - *name = 0; - *index = UINT_MAX; - - QObjectWrapper *that = static_cast(m); - - if (!that->m_object) - return QV4::Object::advanceIterator(m, it, name, index, attributes); - - const QMetaObject *mo = that->m_object->metaObject(); - const int propertyCount = mo->propertyCount(); - if (it->arrayIndex < propertyCount) { - *name = that->engine()->newString(QString::fromUtf8(mo->property(it->arrayIndex).name())); - ++it->arrayIndex; - if (attributes) - *attributes = QV4::Attr_Data; - it->tmpDynamicProperty.value = that->get(*name); - return &it->tmpDynamicProperty; - } - const int methodCount = mo->methodCount(); - if (it->arrayIndex < propertyCount + methodCount) { - *name = that->engine()->newString(QString::fromUtf8(mo->method(it->arrayIndex - propertyCount).name())); - ++it->arrayIndex; - if (attributes) - *attributes = QV4::Attr_Data; - it->tmpDynamicProperty.value = that->get(*name); - return &it->tmpDynamicProperty; - } - return QV4::Object::advanceIterator(m, it, name, index, attributes); -} - -namespace QV4 { - -struct QObjectSlotDispatcher : public QtPrivate::QSlotObjectBase -{ - QV4::PersistentValue function; - QV4::PersistentValue thisObject; - int signalIndex; - - QObjectSlotDispatcher() - : QtPrivate::QSlotObjectBase(&impl) - , signalIndex(-1) - {} - - static void impl(int which, QSlotObjectBase *this_, QObject *r, void **metaArgs, bool *ret) - { - switch (which) { - case Destroy: { - delete static_cast(this_); - } - break; - case Call: { - QObjectSlotDispatcher *This = static_cast(this_); - QVarLengthArray dummy; - int *argsTypes = QQmlPropertyCache::methodParameterTypes(r, This->signalIndex, dummy, 0); - - int argCount = argsTypes ? argsTypes[0]:0; - - QV4::FunctionObject *f = This->function.value().asFunctionObject(); - QV4::ExecutionEngine *v4 = f->internalClass->engine; - QV4::ExecutionContext *ctx = v4->current; - - QVarLengthArray args(argCount); - for (int ii = 0; ii < argCount; ++ii) { - int type = argsTypes[ii + 1]; - if (type == qMetaTypeId()) { - args[ii] = v4->v8Engine->fromVariant(*((QVariant *)metaArgs[ii + 1])); - } else { - args[ii] = v4->v8Engine->fromVariant(QVariant(type, metaArgs[ii + 1])); - } - } - - try { - f->call(This->thisObject.isEmpty() ? Value::fromObject(v4->globalObject) : This->thisObject.value(), args.data(), argCount); - } catch (QV4::Exception &e) { - e.accept(ctx); - QQmlError error; - QQmlExpressionPrivate::exceptionToError(e, error); - if (error.description().isEmpty()) - error.setDescription(QString(QLatin1String("Unknown exception occurred during evaluation of connected function: %1")).arg(f->name->toQString())); - QQmlEnginePrivate::get(v4->v8Engine->engine())->warning(error); - } - } - break; - case Compare: { - QObjectSlotDispatcher *connection = static_cast(this_); - if (connection->function.isEmpty()) { - *ret = false; - return; - } - - // This is tricky. Normally the metaArgs[0] pointer is a pointer to the _function_ - // for the new-style QObject::connect. Here we use the engine pointer as sentinel - // to distinguish those type of QSlotObjectBase connections from our QML connections. - QV4::ExecutionEngine *v4 = reinterpret_cast(metaArgs[0]); - if (v4 != connection->function.engine()) { - *ret = false; - return; - } - - QV4::Value function = *reinterpret_cast(metaArgs[1]); - QV4::Value thisObject = *reinterpret_cast(metaArgs[2]); - QObject *receiverToDisconnect = reinterpret_cast(metaArgs[3]); - int slotIndexToDisconnect = *reinterpret_cast(metaArgs[4]); - - if (slotIndexToDisconnect != -1) { - // This is a QObject function wrapper - if (connection->thisObject.isEmpty() == thisObject.isEmpty() && - (connection->thisObject.isEmpty() || __qmljs_strict_equal(connection->thisObject, thisObject))) { - - QPair connectedFunctionData = extractQtMethod(connection->function.value().asFunctionObject()); - if (connectedFunctionData.first == receiverToDisconnect && - connectedFunctionData.second == slotIndexToDisconnect) { - *ret = true; - return; - } - } - } else { - // This is a normal JS function - if (__qmljs_strict_equal(connection->function, function) && - connection->thisObject.isEmpty() == thisObject.isEmpty() && - (connection->thisObject.isEmpty() || __qmljs_strict_equal(connection->thisObject, thisObject))) { - *ret = true; - return; - } - } - - *ret = false; - } - break; - case NumOperations: - break; - } - }; -}; - -} // namespace QV4 - -Value QObjectWrapper::method_connect(SimpleCallContext *ctx) -{ - if (ctx->argumentCount == 0) - V4THROW_ERROR("Function.prototype.connect: no arguments given"); - - QPair signalInfo = extractQtSignal(ctx->thisObject); - QObject *signalObject = signalInfo.first; - int signalIndex = signalInfo.second; - - if (signalIndex < 0) - V4THROW_ERROR("Function.prototype.connect: this object is not a signal"); - - if (!signalObject) - V4THROW_ERROR("Function.prototype.connect: cannot connect to deleted QObject"); - - if (signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal) - V4THROW_ERROR("Function.prototype.connect: this object is not a signal"); - - QV4::QObjectSlotDispatcher *slot = new QV4::QObjectSlotDispatcher; - slot->signalIndex = signalIndex; - - if (ctx->argumentCount == 1) { - slot->function = ctx->arguments[0]; - } else if (ctx->argumentCount >= 2) { - slot->thisObject = ctx->arguments[0]; - slot->function = ctx->arguments[1]; - } - - if (!slot->function.value().asFunctionObject()) - V4THROW_ERROR("Function.prototype.connect: target is not a function"); - - if (!slot->thisObject.isEmpty() && !slot->thisObject.value().isObject()) - V4THROW_ERROR("Function.prototype.connect: target this is not an object"); - - QObjectPrivate::connect(signalObject, signalIndex, slot, Qt::AutoConnection); - - return QV4::Value::undefinedValue(); -} - -Value QObjectWrapper::method_disconnect(SimpleCallContext *ctx) -{ - if (ctx->argumentCount == 0) - V4THROW_ERROR("Function.prototype.disconnect: no arguments given"); - - QPair signalInfo = extractQtSignal(ctx->thisObject); - QObject *signalObject = signalInfo.first; - int signalIndex = signalInfo.second; - - if (signalIndex == -1) - V4THROW_ERROR("Function.prototype.disconnect: this object is not a signal"); - - if (!signalObject) - V4THROW_ERROR("Function.prototype.disconnect: cannot disconnect from deleted QObject"); - - if (signalIndex < 0 || signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal) - V4THROW_ERROR("Function.prototype.disconnect: this object is not a signal"); - - QV4::Value functionValue = QV4::Value::emptyValue(); - QV4::Value functionThisValue = QV4::Value::emptyValue(); - - if (ctx->argumentCount == 1) { - functionValue = ctx->arguments[0]; - } else if (ctx->argumentCount >= 2) { - functionThisValue = ctx->arguments[0]; - functionValue = ctx->arguments[1]; - } - - if (!functionValue.asFunctionObject()) - V4THROW_ERROR("Function.prototype.disconnect: target is not a function"); - - if (!functionThisValue.isEmpty() && !functionThisValue.isObject()) - V4THROW_ERROR("Function.prototype.disconnect: target this is not an object"); - - QPair functionData = extractQtMethod(functionValue.asFunctionObject()); - - void *a[] = { - ctx->engine, - &functionValue, - &functionThisValue, - functionData.first, - &functionData.second - }; - - QObjectPrivate::disconnect(signalObject, signalIndex, reinterpret_cast(&a)); - - return QV4::Value::undefinedValue(); -} - -static void markChildQObjectsRecursively(QObject *parent) -{ - const QObjectList &children = parent->children(); - for (int i = 0; i < children.count(); ++i) { - QObject *child = children.at(i); - QQmlData *ddata = QQmlData::get(child, /*create*/false); - if (ddata) - ddata->jsWrapper.markOnce(); - markChildQObjectsRecursively(child); - } -} - -void QObjectWrapper::markObjects(Managed *that) -{ - QObjectWrapper *This = static_cast(that); - - if (QObject *o = This->m_object.data()) { - QQmlVMEMetaObject *vme = QQmlVMEMetaObject::get(o); - if (vme) - vme->mark(); - - // Children usually don't need to be marked, the gc keeps them alive. - // But in the rare case of a "floating" QObject without a parent that - // _gets_ marked (we've been called here!) then we also need to - // propagate the marking down to the children recursively. - if (!o->parent()) - markChildQObjectsRecursively(o); - } - - QV4::Object::markObjects(that); -} - -namespace { - struct QObjectDeleter : public QV4::GCDeletable - { - QObjectDeleter(QObject *o) - : m_objectToDelete(o) - {} - ~QObjectDeleter() - { - QQmlData *ddata = QQmlData::get(m_objectToDelete, false); - if (ddata && ddata->ownContext && ddata->context) - ddata->context->emitDestruction(); - // This object is notionally destroyed now - ddata->isQueuedForDeletion = true; - if (lastCall) - delete m_objectToDelete; - else - m_objectToDelete->deleteLater(); - } - - QObject *m_objectToDelete; - }; -} - -void QObjectWrapper::collectDeletables(Managed *m, GCDeletable **deletable) -{ - QObjectWrapper *This = static_cast(m); - QPointer &object = This->m_object; - if (!object) - return; - - QQmlData *ddata = QQmlData::get(object, false); - if (!ddata) - return; - - if (object->parent() || ddata->indestructible) - return; - - QObjectDeleter *deleter = new QObjectDeleter(object); - object = 0; - deleter->next = *deletable; - *deletable = deleter; -} - -DEFINE_MANAGED_VTABLE_WITH_DELETABLES(QObjectWrapper); - -// XXX TODO: Need to review all calls to QQmlEngine *engine() to confirm QObjects work -// correctly in a worker thread - -namespace { - -template -class MaxSizeOf8 { - template - struct SMax { - char dummy[sizeof(Z) > sizeof(X) ? sizeof(Z) : sizeof(X)]; - }; -public: - static const size_t Size = sizeof(SMax > > > > > >); -}; - -struct CallArgument { - inline CallArgument(); - inline ~CallArgument(); - inline void *dataPtr(); - - inline void initAsType(int type); - inline void fromValue(int type, QV8Engine *, const QV4::Value&); - inline QV4::Value toValue(QV8Engine *); - -private: - CallArgument(const CallArgument &); - - inline void cleanup(); - - union { - float floatValue; - double doubleValue; - quint32 intValue; - bool boolValue; - QObject *qobjectPtr; - - char allocData[MaxSizeOf8, - QJSValue, - QQmlV4Handle, - QJsonArray, - QJsonObject, - QJsonValue>::Size]; - qint64 q_for_alignment; - }; - - // Pointers to allocData - union { - QString *qstringPtr; - QVariant *qvariantPtr; - QList *qlistPtr; - QJSValue *qjsValuePtr; - QQmlV4Handle *handlePtr; - QJsonArray *jsonArrayPtr; - QJsonObject *jsonObjectPtr; - QJsonValue *jsonValuePtr; - }; - - int type; -}; -} - -namespace { -struct CallArgs -{ - CallArgs(int length, QV4::Value *args) : _length(length), _args(args) {} - int Length() const { return _length; } - QV4::Value operator[](int idx) { return _args[idx]; } - -private: - int _length; - QV4::Value *_args; -}; -} - -static QV4::Value CallMethod(QObject *object, int index, int returnType, int argCount, - int *argTypes, QV8Engine *engine, CallArgs &callArgs) -{ - if (argCount > 0) { - - // Special handling is required for value types. - // We need to save the current value in a temporary, - // and reapply it after converting all arguments. - // This avoids the "overwriting copy-value-type-value" - // problem during Q_INVOKABLE function invocation. - QQmlValueType *valueTypeObject = qobject_cast(object); - QVariant valueTypeValue; - if (valueTypeObject) - valueTypeValue = valueTypeObject->value(); - - // Convert all arguments. - QVarLengthArray args(argCount + 1); - args[0].initAsType(returnType); - for (int ii = 0; ii < argCount; ++ii) - args[ii + 1].fromValue(argTypes[ii], engine, callArgs[ii]); - QVarLengthArray argData(args.count()); - for (int ii = 0; ii < args.count(); ++ii) - argData[ii] = args[ii].dataPtr(); - - // Reinstate saved value type object value if required. - if (valueTypeObject) - valueTypeObject->setValue(valueTypeValue); - - QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, argData.data()); - - return args[0].toValue(engine); - - } else if (returnType != QMetaType::Void) { - - CallArgument arg; - arg.initAsType(returnType); - - void *args[] = { arg.dataPtr() }; - - QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args); - - return arg.toValue(engine); - - } else { - - void *args[] = { 0 }; - QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args); - return QV4::Value::undefinedValue(); - - } -} - -/*! - Returns the match score for converting \a actual to be of type \a conversionType. A - zero score means "perfect match" whereas a higher score is worse. - - The conversion table is copied out of the \l QScript::callQtMethod() function. -*/ -static int MatchScore(const QV4::Value &actual, int conversionType) -{ - if (actual.isNumber()) { - switch (conversionType) { - case QMetaType::Double: - return 0; - case QMetaType::Float: - return 1; - case QMetaType::LongLong: - case QMetaType::ULongLong: - return 2; - case QMetaType::Long: - case QMetaType::ULong: - return 3; - case QMetaType::Int: - case QMetaType::UInt: - return 4; - case QMetaType::Short: - case QMetaType::UShort: - return 5; - break; - case QMetaType::Char: - case QMetaType::UChar: - return 6; - case QMetaType::QJsonValue: - return 5; - default: - return 10; - } - } else if (actual.isString()) { - switch (conversionType) { - case QMetaType::QString: - return 0; - case QMetaType::QJsonValue: - return 5; - default: - return 10; - } - } else if (actual.isBoolean()) { - switch (conversionType) { - case QMetaType::Bool: - return 0; - case QMetaType::QJsonValue: - return 5; - default: - return 10; - } - } else if (actual.asDateObject()) { - switch (conversionType) { - case QMetaType::QDateTime: - return 0; - case QMetaType::QDate: - return 1; - case QMetaType::QTime: - return 2; - default: - return 10; - } - } else if (actual.as()) { - switch (conversionType) { - case QMetaType::QRegExp: - return 0; - default: - return 10; - } - } else if (actual.asArrayObject()) { - switch (conversionType) { - case QMetaType::QJsonArray: - return 3; - case QMetaType::QStringList: - case QMetaType::QVariantList: - return 5; - case QMetaType::QVector4D: - case QMetaType::QMatrix4x4: - return 6; - case QMetaType::QVector3D: - return 7; - default: - return 10; - } - } else if (actual.isNull()) { - switch (conversionType) { - case QMetaType::VoidStar: - case QMetaType::QObjectStar: - case QMetaType::QJsonValue: - return 0; - default: { - const char *typeName = QMetaType::typeName(conversionType); - if (typeName && typeName[strlen(typeName) - 1] == '*') - return 0; - else - return 10; - } - } - } else if (QV4::Object *obj = actual.asObject()) { - QV8Engine *engine = obj->engine()->v8Engine; - - if (QV4::VariantObject *v = obj->as()) { - if (conversionType == qMetaTypeId()) - return 0; - if (engine->toVariant(actual, -1).userType() == conversionType) - return 0; - else - return 10; - } - - if (obj->as()) { - switch (conversionType) { - case QMetaType::QObjectStar: - return 0; - default: - return 10; - } - } - - if (obj->as()) { - if (engine->toVariant(actual, -1).userType() == conversionType) - return 0; - return 10; - } else if (conversionType == QMetaType::QJsonObject) { - return 5; - } else { - return 10; - } - - } else { - return 10; - } -} - -static inline int QMetaObject_methods(const QMetaObject *metaObject) -{ - struct Private - { - int revision; - int className; - int classInfoCount, classInfoData; - int methodCount, methodData; - }; - - return reinterpret_cast(metaObject->d.data)->methodCount; -} - -/*! -Returns the next related method, if one, or 0. -*/ -static const QQmlPropertyData * RelatedMethod(QObject *object, - const QQmlPropertyData *current, - QQmlPropertyData &dummy) -{ - QQmlPropertyCache *cache = QQmlData::get(object)->propertyCache; - if (!current->isOverload()) - return 0; - - Q_ASSERT(!current->overrideIndexIsProperty); - - if (cache) { - return cache->method(current->overrideIndex); - } else { - const QMetaObject *mo = object->metaObject(); - int methodOffset = mo->methodCount() - QMetaObject_methods(mo); - - while (methodOffset > current->overrideIndex) { - mo = mo->superClass(); - methodOffset -= QMetaObject_methods(mo); - } - - QMetaMethod method = mo->method(current->overrideIndex); - dummy.load(method); - - // Look for overloaded methods - QByteArray methodName = method.name(); - for (int ii = current->overrideIndex - 1; ii >= methodOffset; --ii) { - if (methodName == mo->method(ii).name()) { - dummy.setFlags(dummy.getFlags() | QQmlPropertyData::IsOverload); - dummy.overrideIndexIsProperty = 0; - dummy.overrideIndex = ii; - return &dummy; - } - } - - return &dummy; - } -} - -static QV4::Value CallPrecise(QObject *object, const QQmlPropertyData &data, - QV8Engine *engine, CallArgs &callArgs) -{ - QByteArray unknownTypeError; - - int returnType = QQmlPropertyCache::methodReturnType(object, data, &unknownTypeError); - - if (returnType == QMetaType::UnknownType) { - QString typeName = QString::fromLatin1(unknownTypeError); - QString error = QString::fromLatin1("Unknown method return type: %1").arg(typeName); - QV8Engine::getV4(engine)->current->throwError(error); - } - - if (data.hasArguments()) { - - int *args = 0; - QVarLengthArray dummy; - - args = QQmlPropertyCache::methodParameterTypes(object, data.coreIndex, dummy, - &unknownTypeError); - - if (!args) { - QString typeName = QString::fromLatin1(unknownTypeError); - QString error = QString::fromLatin1("Unknown method parameter type: %1").arg(typeName); - QV8Engine::getV4(engine)->current->throwError(error); - } - - if (args[0] > callArgs.Length()) { - QString error = QLatin1String("Insufficient arguments"); - QV8Engine::getV4(engine)->current->throwError(error); - } - - return CallMethod(object, data.coreIndex, returnType, args[0], args + 1, engine, callArgs); - - } else { - - return CallMethod(object, data.coreIndex, returnType, 0, 0, engine, callArgs); - - } -} - -/*! -Resolve the overloaded method to call. The algorithm works conceptually like this: - 1. Resolve the set of overloads it is *possible* to call. - Impossible overloads include those that have too many parameters or have parameters - of unknown type. - 2. Filter the set of overloads to only contain those with the closest number of - parameters. - For example, if we are called with 3 parameters and there are 2 overloads that - take 2 parameters and one that takes 3, eliminate the 2 parameter overloads. - 3. Find the best remaining overload based on its match score. - If two or more overloads have the same match score, call the last one. The match - score is constructed by adding the matchScore() result for each of the parameters. -*/ -static QV4::Value CallOverloaded(QObject *object, const QQmlPropertyData &data, - QV8Engine *engine, CallArgs &callArgs) -{ - int argumentCount = callArgs.Length(); - - const QQmlPropertyData *best = 0; - int bestParameterScore = INT_MAX; - int bestMatchScore = INT_MAX; - - // Special handling is required for value types. - // We need to save the current value in a temporary, - // and reapply it after converting all arguments. - // This avoids the "overwriting copy-value-type-value" - // problem during Q_INVOKABLE function invocation. - QQmlValueType *valueTypeObject = qobject_cast(object); - QVariant valueTypeValue; - if (valueTypeObject) - valueTypeValue = valueTypeObject->value(); - - QQmlPropertyData dummy; - const QQmlPropertyData *attempt = &data; - - do { - QVarLengthArray dummy; - int methodArgumentCount = 0; - int *methodArgTypes = 0; - if (attempt->hasArguments()) { - typedef QQmlPropertyCache PC; - int *args = PC::methodParameterTypes(object, attempt->coreIndex, dummy, 0); - if (!args) // Must be an unknown argument - continue; - - methodArgumentCount = args[0]; - methodArgTypes = args + 1; - } - - if (methodArgumentCount > argumentCount) - continue; // We don't have sufficient arguments to call this method - - int methodParameterScore = argumentCount - methodArgumentCount; - if (methodParameterScore > bestParameterScore) - continue; // We already have a better option - - int methodMatchScore = 0; - for (int ii = 0; ii < methodArgumentCount; ++ii) - methodMatchScore += MatchScore(callArgs[ii], methodArgTypes[ii]); - - if (bestParameterScore > methodParameterScore || bestMatchScore > methodMatchScore) { - best = attempt; - bestParameterScore = methodParameterScore; - bestMatchScore = methodMatchScore; - } - - if (bestParameterScore == 0 && bestMatchScore == 0) - break; // We can't get better than that - - } while((attempt = RelatedMethod(object, attempt, dummy)) != 0); - - if (best) { - if (valueTypeObject) - valueTypeObject->setValue(valueTypeValue); - return CallPrecise(object, *best, engine, callArgs); - } else { - QString error = QLatin1String("Unable to determine callable overload. Candidates are:"); - const QQmlPropertyData *candidate = &data; - while (candidate) { - error += QLatin1String("\n ") + - QString::fromUtf8(object->metaObject()->method(candidate->coreIndex).methodSignature().constData()); - candidate = RelatedMethod(object, candidate, dummy); - } - - QV8Engine::getV4(engine)->current->throwError(error); - } -} - -CallArgument::CallArgument() -: type(QVariant::Invalid) -{ -} - -CallArgument::~CallArgument() -{ - cleanup(); -} - -void CallArgument::cleanup() -{ - if (type == QMetaType::QString) { - qstringPtr->~QString(); - } else if (type == -1 || type == QMetaType::QVariant) { - qvariantPtr->~QVariant(); - } else if (type == qMetaTypeId()) { - qjsValuePtr->~QJSValue(); - } else if (type == qMetaTypeId >()) { - qlistPtr->~QList(); - } else if (type == QMetaType::QJsonArray) { - jsonArrayPtr->~QJsonArray(); - } else if (type == QMetaType::QJsonObject) { - jsonObjectPtr->~QJsonObject(); - } else if (type == QMetaType::QJsonValue) { - jsonValuePtr->~QJsonValue(); - } -} - -void *CallArgument::dataPtr() -{ - if (type == -1) - return qvariantPtr->data(); - else - return (void *)&allocData; -} - -void CallArgument::initAsType(int callType) -{ - if (type != 0) { cleanup(); type = 0; } - if (callType == QMetaType::UnknownType) return; - - if (callType == qMetaTypeId()) { - qjsValuePtr = new (&allocData) QJSValue(); - type = callType; - } else if (callType == QMetaType::Int || - callType == QMetaType::UInt || - callType == QMetaType::Bool || - callType == QMetaType::Double || - callType == QMetaType::Float) { - type = callType; - } else if (callType == QMetaType::QObjectStar) { - qobjectPtr = 0; - type = callType; - } else if (callType == QMetaType::QString) { - qstringPtr = new (&allocData) QString(); - type = callType; - } else if (callType == QMetaType::QVariant) { - type = callType; - qvariantPtr = new (&allocData) QVariant(); - } else if (callType == qMetaTypeId >()) { - type = callType; - qlistPtr = new (&allocData) QList(); - } else if (callType == qMetaTypeId()) { - type = callType; - handlePtr = new (&allocData) QQmlV4Handle; - } else if (callType == QMetaType::QJsonArray) { - type = callType; - jsonArrayPtr = new (&allocData) QJsonArray(); - } else if (callType == QMetaType::QJsonObject) { - type = callType; - jsonObjectPtr = new (&allocData) QJsonObject(); - } else if (callType == QMetaType::QJsonValue) { - type = callType; - jsonValuePtr = new (&allocData) QJsonValue(); - } else if (callType == QMetaType::Void) { - type = -1; - qvariantPtr = new (&allocData) QVariant(); - } else { - type = -1; - qvariantPtr = new (&allocData) QVariant(callType, (void *)0); - } -} - -void CallArgument::fromValue(int callType, QV8Engine *engine, const QV4::Value &value) -{ - if (type != 0) { cleanup(); type = 0; } - - if (callType == qMetaTypeId()) { - qjsValuePtr = new (&allocData) QJSValue(new QJSValuePrivate(QV8Engine::getV4(engine), value)); - type = qMetaTypeId(); - } else if (callType == QMetaType::Int) { - intValue = quint32(value.toInt32()); - type = callType; - } else if (callType == QMetaType::UInt) { - intValue = quint32(value.toUInt32()); - type = callType; - } else if (callType == QMetaType::Bool) { - boolValue = value.toBoolean(); - type = callType; - } else if (callType == QMetaType::Double) { - doubleValue = double(value.toNumber()); - type = callType; - } else if (callType == QMetaType::Float) { - floatValue = float(value.toNumber()); - type = callType; - } else if (callType == QMetaType::QString) { - if (value.isNull() || value.isUndefined()) - qstringPtr = new (&allocData) QString(); - else - qstringPtr = new (&allocData) QString(value.toQString()); - type = callType; - } else if (callType == QMetaType::QObjectStar) { - qobjectPtr = 0; - if (QV4::QObjectWrapper *qobjectWrapper = value.as()) - qobjectPtr = qobjectWrapper->object(); - type = callType; - } else if (callType == qMetaTypeId()) { - qvariantPtr = new (&allocData) QVariant(engine->toVariant(value, -1)); - type = callType; - } else if (callType == qMetaTypeId >()) { - qlistPtr = new (&allocData) QList(); - if (QV4::ArrayObject *array = value.asArrayObject()) { - uint32_t length = array->arrayLength(); - for (uint32_t ii = 0; ii < length; ++ii) { - QObject *o = 0; - if (QV4::QObjectWrapper *qobjectWrapper = array->getIndexed(ii).as()) - o = qobjectWrapper->object(); - qlistPtr->append(o); - } - } else { - QObject *o = 0; - if (QV4::QObjectWrapper *qobjectWrapper = value.as()) - o = qobjectWrapper->object(); - qlistPtr->append(o); - } - type = callType; - } else if (callType == qMetaTypeId()) { - handlePtr = new (&allocData) QQmlV4Handle(QQmlV4Handle(value)); - type = callType; - } else if (callType == QMetaType::QJsonArray) { - jsonArrayPtr = new (&allocData) QJsonArray(QV4::JsonObject::toJsonArray(value.asArrayObject())); - type = callType; - } else if (callType == QMetaType::QJsonObject) { - jsonObjectPtr = new (&allocData) QJsonObject(QV4::JsonObject::toJsonObject(value.asObject())); - type = callType; - } else if (callType == QMetaType::QJsonValue) { - jsonValuePtr = new (&allocData) QJsonValue(QV4::JsonObject::toJsonValue(value)); - type = callType; - } else if (callType == QMetaType::Void) { - *qvariantPtr = QVariant(); - } else { - qvariantPtr = new (&allocData) QVariant(); - type = -1; - - QQmlEnginePrivate *ep = engine->engine() ? QQmlEnginePrivate::get(engine->engine()) : 0; - QVariant v = engine->toVariant(value, -1); // why -1 instead of callType? - - if (v.userType() == callType) { - *qvariantPtr = v; - } else if (v.canConvert(callType)) { - *qvariantPtr = v; - qvariantPtr->convert(callType); - } else if (QV4::SequencePrototype::isSequenceType(callType) && v.userType() == qMetaTypeId()) { - // convert the JS array to a sequence of the correct type. - QVariant seqV = engine->toVariant(value, callType); - *qvariantPtr = seqV; - } else { - QQmlMetaObject mo = ep ? ep->rawMetaObjectForType(callType) : QQmlMetaObject(); - if (!mo.isNull()) { - QObject *obj = ep->toQObject(v); - - if (obj != 0 && !QQmlMetaObject::canConvert(obj, mo)) - obj = 0; - - *qvariantPtr = QVariant(callType, &obj); - } else { - *qvariantPtr = QVariant(callType, (void *)0); - } - } - } -} - -QV4::Value CallArgument::toValue(QV8Engine *engine) -{ - QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine); - if (type == qMetaTypeId()) { - return QJSValuePrivate::get(*qjsValuePtr)->getValue(v4); - } else if (type == QMetaType::Int) { - return QV4::Value::fromInt32(int(intValue)); - } else if (type == QMetaType::UInt) { - return QV4::Value::fromUInt32(intValue); - } else if (type == QMetaType::Bool) { - return QV4::Value::fromBoolean(boolValue); - } else if (type == QMetaType::Double) { - return QV4::Value::fromDouble(doubleValue); - } else if (type == QMetaType::Float) { - return QV4::Value::fromDouble(floatValue); - } else if (type == QMetaType::QString) { - return engine->toString(*qstringPtr); - } else if (type == QMetaType::QObjectStar) { - QObject *object = qobjectPtr; - if (object) - QQmlData::get(object, true)->setImplicitDestructible(); - return QV4::QObjectWrapper::wrap(v4, object); - } else if (type == qMetaTypeId >()) { - // XXX Can this be made more by using Array as a prototype and implementing - // directly against QList? - QList &list = *qlistPtr; - QV4::ArrayObject *array = v4->newArrayObject(); - array->arrayReserve(list.count()); - array->arrayDataLen = list.count(); - for (int ii = 0; ii < list.count(); ++ii) - array->arrayData[ii].value = QV4::QObjectWrapper::wrap(v4, list.at(ii)); - array->setArrayLengthUnchecked(list.count()); - return QV4::Value::fromObject(array); - } else if (type == qMetaTypeId()) { - return handlePtr->toValue(); - } else if (type == QMetaType::QJsonArray) { - return QV4::JsonObject::fromJsonArray(v4, *jsonArrayPtr); - } else if (type == QMetaType::QJsonObject) { - return QV4::JsonObject::fromJsonObject(v4, *jsonObjectPtr); - } else if (type == QMetaType::QJsonValue) { - return QV4::JsonObject::fromJsonValue(v4, *jsonValuePtr); - } else if (type == -1 || type == qMetaTypeId()) { - QVariant value = *qvariantPtr; - QV4::Value rv = engine->fromVariant(value); - if (QV4::QObjectWrapper *qobjectWrapper = rv.as()) { - if (QObject *object = qobjectWrapper->object()) - QQmlData::get(object, true)->setImplicitDestructible(); - } - return rv; - } else { - return QV4::Value::undefinedValue(); - } -} - -Value QObjectMethod::create(ExecutionContext *scope, QObject *object, int index, const Value &qmlGlobal) -{ - return Value::fromObject(new (scope->engine->memoryManager) QObjectMethod(scope, object, index, qmlGlobal)); -} - -QObjectMethod::QObjectMethod(ExecutionContext *scope, QObject *object, int index, const Value &qmlGlobal) - : FunctionObject(scope) - , m_object(object) - , m_index(index) - , m_qmlGlobal(qmlGlobal) -{ - vtbl = &static_vtbl; - subtype = WrappedQtMethod; -} - -QV4::Value QObjectMethod::method_toString(QV4::ExecutionContext *ctx) -{ - QString result; - if (m_object) { - QString objectName = m_object->objectName(); - - result += QString::fromUtf8(m_object->metaObject()->className()); - result += QLatin1String("(0x"); - result += QString::number((quintptr)m_object.data(),16); - - if (!objectName.isEmpty()) { - result += QLatin1String(", \""); - result += objectName; - result += QLatin1Char('\"'); - } - - result += QLatin1Char(')'); - } else { - result = QLatin1String("null"); - } - - return QV4::Value::fromString(ctx, result); -} - -QV4::Value QObjectMethod::method_destroy(QV4::ExecutionContext *ctx, Value *args, int argc) -{ - if (!m_object) - return QV4::Value::undefinedValue(); - if (QQmlData::keepAliveDuringGarbageCollection(m_object)) - ctx->throwError(QStringLiteral("Invalid attempt to destroy() an indestructible object")); - - int delay = 0; - if (argc > 0) - delay = args[0].toUInt32(); - - if (delay > 0) - QTimer::singleShot(delay, m_object, SLOT(deleteLater())); - else - m_object->deleteLater(); - - return QV4::Value::undefinedValue(); -} - -Value QObjectMethod::call(Managed *m, const Value &thisObject, Value *args, int argc) -{ - QObjectMethod *This = static_cast(m); - return This->callInternal(thisObject, args, argc); -} - -Value QObjectMethod::callInternal(const Value &, Value *args, int argc) -{ - ExecutionContext *context = engine()->current; - if (m_index == DestroyMethod) - return method_destroy(context, args, argc); - else if (m_index == ToStringMethod) - return method_toString(context); - - QObject *object = m_object.data(); - if (!object) - return QV4::Value::undefinedValue(); - - QQmlData *ddata = QQmlData::get(object); - if (!ddata) - return QV4::Value::undefinedValue(); - - QV8Engine *v8Engine = context->engine->v8Engine; - - QQmlPropertyData method; - - if (QQmlData *ddata = static_cast(QObjectPrivate::get(object)->declarativeData)) { - if (ddata->propertyCache) { - QQmlPropertyData *d = ddata->propertyCache->method(m_index); - if (!d) - return QV4::Value::undefinedValue(); - method = *d; - } - } - - if (method.coreIndex == -1) { - method.load(object->metaObject()->method(m_index)); - - if (method.coreIndex == -1) - return QV4::Value::undefinedValue(); - } - - if (method.isV4Function()) { - QV4::Value rv = QV4::Value::undefinedValue(); - - QQmlV4Function func(argc, args, &rv, m_qmlGlobal.value(), - QmlContextWrapper::getContext(m_qmlGlobal.value()), - v8Engine); - QQmlV4Function *funcptr = &func; - - void *args[] = { 0, &funcptr }; - QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, method.coreIndex, args); - - return rv; - } - - CallArgs callArgs(argc, args); - if (!method.isOverload()) { - return CallPrecise(object, method, v8Engine, callArgs); - } else { - return CallOverloaded(object, method, v8Engine, callArgs); - } -} - -DEFINE_MANAGED_VTABLE(QObjectMethod); - -QmlSignalHandler::QmlSignalHandler(ExecutionEngine *engine, QObject *object, int signalIndex) - : Object(engine) - , m_object(object) - , m_signalIndex(signalIndex) -{ - vtbl = &static_vtbl; - prototype = engine->objectPrototype; -} - -DEFINE_MANAGED_VTABLE(QmlSignalHandler); - -void MultiplyWrappedQObjectMap::insert(QObject *key, Object *value) -{ - QHash::insert(key, value); - connect(key, SIGNAL(destroyed(QObject*)), this, SLOT(removeDestroyedObject(QObject*))); -} - -MultiplyWrappedQObjectMap::Iterator MultiplyWrappedQObjectMap::erase(MultiplyWrappedQObjectMap::Iterator it) -{ - disconnect(it.key(), SIGNAL(destroyed(QObject*)), this, SLOT(removeDestroyedObject(QObject*))); - return QHash::erase(it); -} - -void MultiplyWrappedQObjectMap::remove(QObject *key) -{ - Iterator it = find(key); - if (it == end()) - return; - erase(it); -} - -void MultiplyWrappedQObjectMap::removeDestroyedObject(QObject *object) -{ - QHash::remove(object); -} - -QT_END_NAMESPACE - diff --git a/src/qml/qml/v4/qv4qobjectwrapper_p.h b/src/qml/qml/v4/qv4qobjectwrapper_p.h deleted file mode 100644 index 6580d19fe9..0000000000 --- a/src/qml/qml/v4/qv4qobjectwrapper_p.h +++ /dev/null @@ -1,201 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QV4QOBJECTWRAPPER_P_H -#define QV4QOBJECTWRAPPER_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -QT_BEGIN_NAMESPACE - -class QObject; -class QQmlData; -class QQmlPropertyCache; -class QQmlPropertyData; - -namespace QV4 { -struct QObjectSlotDispatcher; - -struct Q_QML_EXPORT QObjectWrapper : public QV4::Object -{ - Q_MANAGED - - enum RevisionMode { IgnoreRevision, CheckRevision }; - - static void initializeBindings(ExecutionEngine *engine); - - QObject *object() const { return m_object.data(); } - - Value getQmlProperty(ExecutionContext *ctx, QQmlContextData *qmlContext, String *name, RevisionMode revisionMode, bool *hasProperty = 0, bool includeImports = false); - static Value getQmlProperty(ExecutionContext *ctx, QQmlContextData *qmlContext, QObject *object, String *name, RevisionMode revisionMode, bool *hasProperty = 0); - - static bool setQmlProperty(ExecutionContext *ctx, QQmlContextData *qmlContext, QObject *object, String *name, RevisionMode revisionMode, const Value &value); - - static Value wrap(ExecutionEngine *engine, QObject *object); - - using Object::get; - -private: - static Value create(ExecutionEngine *engine, QQmlData *ddata, QObject *object); - - QObjectWrapper(ExecutionEngine *engine, QObject *object); - - QQmlPropertyData *findProperty(ExecutionEngine *engine, QQmlContextData *qmlContext, String *name, RevisionMode revisionMode, QQmlPropertyData *local) const; - - QPointer m_object; - String *m_destroy; - String *m_toString; - - static Value get(Managed *m, String *name, bool *hasProperty); - static void put(Managed *m, String *name, const Value &value); - static PropertyAttributes query(const Managed *, String *name); - static Property *advanceIterator(Managed *m, ObjectIterator *it, String **name, uint *index, PropertyAttributes *attributes); - static void markObjects(Managed *that); - static void collectDeletables(Managed *m, GCDeletable **deletable); - static void destroy(Managed *that) - { - static_cast(that)->~QObjectWrapper(); - } - - static Value method_connect(SimpleCallContext *ctx); - static Value method_disconnect(SimpleCallContext *ctx); -}; - -struct QObjectMethod : public QV4::FunctionObject -{ - Q_MANAGED - - enum { DestroyMethod = -1, ToStringMethod = -2 }; - - static Value create(QV4::ExecutionContext *scope, QObject *object, int index, const Value &qmlGlobal = Value::undefinedValue()); - - int methodIndex() const { return m_index; } - QObject *object() const { return m_object.data(); } - -private: - QObjectMethod(QV4::ExecutionContext *scope, QObject *object, int index, const QV4::Value &qmlGlobal); - - QV4::Value method_toString(QV4::ExecutionContext *ctx); - QV4::Value method_destroy(QV4::ExecutionContext *ctx, Value *args, int argc); - - QPointer m_object; - int m_index; - QV4::PersistentValue m_qmlGlobal; - - static Value call(Managed *, const Value &thisObject, Value *args, int argc); - - Value callInternal(const Value &, Value *args, int argc); - - static void destroy(Managed *that) - { - static_cast(that)->~QObjectMethod(); - } -}; - -struct QmlSignalHandler : public QV4::Object -{ - Q_MANAGED - - QmlSignalHandler(ExecutionEngine *engine, QObject *object, int signalIndex); - - int signalIndex() const { return m_signalIndex; } - QObject *object() const { return m_object.data(); } - -private: - QPointer m_object; - int m_signalIndex; - - static void destroy(Managed *that) - { - static_cast(that)->~QmlSignalHandler(); - } -}; - -class MultiplyWrappedQObjectMap : public QObject, - private QHash -{ - Q_OBJECT -public: - typedef QHash::ConstIterator ConstIterator; - typedef QHash::Iterator Iterator; - - ConstIterator begin() const { return QHash::constBegin(); } - Iterator begin() { return QHash::begin(); } - ConstIterator end() const { return QHash::constEnd(); } - Iterator end() { return QHash::end(); } - - void insert(QObject *key, Object *value); - Object *value(QObject *key) const { return QHash::value(key, 0); } - Iterator erase(Iterator it); - void remove(QObject *key); - -private slots: - void removeDestroyedObject(QObject*); -}; - -} - -QT_END_NAMESPACE - -#endif // QV4QOBJECTWRAPPER_P_H - - diff --git a/src/qml/qml/v4/qv4regexp.cpp b/src/qml/qml/v4/qv4regexp.cpp deleted file mode 100644 index ab01ce7650..0000000000 --- a/src/qml/qml/v4/qv4regexp.cpp +++ /dev/null @@ -1,180 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4regexp_p.h" - -#include "qv4engine_p.h" - -using namespace QV4; - -RegExpCache::~RegExpCache() -{ - for (RegExpCache::Iterator it = begin(), e = end(); - it != e; ++it) - it.value()->m_cache = 0; - clear(); -} - -DEFINE_MANAGED_VTABLE(RegExp); - -uint RegExp::match(const QString &string, int start, uint *matchOffsets) -{ - if (!isValid()) - return JSC::Yarr::offsetNoMatch; - - WTF::String s(string); - -#if ENABLE(YARR_JIT) - if (!m_jitCode.isFallBack() && m_jitCode.has16BitCode()) - return m_jitCode.execute(s.characters16(), start, s.length(), (int*)matchOffsets).start; -#endif - - return JSC::Yarr::interpret(m_byteCode.get(), s.characters16(), string.length(), start, matchOffsets); -} - -RegExp* RegExp::create(ExecutionEngine* engine, const QString& pattern, bool ignoreCase, bool multiline) -{ - RegExpCacheKey key(pattern, ignoreCase, multiline); - - RegExpCache *cache = engine->regExpCache; - if (cache) { - if (RegExp *result = cache->value(key)) - return result; - } - - RegExp *result = new (engine->memoryManager) RegExp(engine, pattern, ignoreCase, multiline); - - if (!cache) - cache = engine->regExpCache = new RegExpCache; - - result->m_cache = cache; - cache->insert(key, result); - - return result; -} - -RegExp::RegExp(ExecutionEngine* engine, const QString &pattern, bool ignoreCase, bool multiline) - : Managed(engine->emptyClass) - , m_pattern(pattern) - , m_cache(0) - , m_subPatternCount(0) - , m_ignoreCase(ignoreCase) - , m_multiLine(multiline) -{ - vtbl = &static_vtbl; - type = Type_RegExpObject; - - if (!engine) - return; - const char* error = 0; - JSC::Yarr::YarrPattern yarrPattern(WTF::String(pattern), ignoreCase, multiline, &error); - if (error) - return; - m_subPatternCount = yarrPattern.m_numSubpatterns; - m_byteCode = JSC::Yarr::byteCompile(yarrPattern, engine->bumperPointerAllocator); -#if ENABLE(YARR_JIT) - if (!yarrPattern.m_containsBackreferences && engine->iselFactory->jitCompileRegexps()) { - JSC::JSGlobalData dummy(engine->regExpAllocator); - JSC::Yarr::jitCompile(yarrPattern, JSC::Yarr::Char16, &dummy, m_jitCode); - } -#endif -} - -RegExp::~RegExp() -{ - if (m_cache) { - RegExpCacheKey key(this); - m_cache->remove(key); - } - _data = 0; -} - -void RegExp::destroy(Managed *that) -{ - static_cast(that)->~RegExp(); -} - -void RegExp::markObjects(Managed *that) -{ -} - -Value RegExp::get(Managed *m, String *name, bool *hasProperty) -{ - return Value::undefinedValue(); -} - -Value RegExp::getIndexed(Managed *m, uint index, bool *hasProperty) -{ - return Value::undefinedValue(); -} - -void RegExp::put(Managed *m, String *name, const Value &value) -{ -} - -void RegExp::putIndexed(Managed *m, uint index, const Value &value) -{ -} - -PropertyAttributes RegExp::query(const Managed *m, String *name) -{ - return Attr_Invalid; -} - -PropertyAttributes RegExp::queryIndexed(const Managed *m, uint index) -{ - return Attr_Invalid; -} - -bool RegExp::deleteProperty(Managed *, String *) -{ - return false; -} - -bool RegExp::deleteIndexedProperty(Managed *m, uint index) -{ - return false; -} - -Property *RegExp::advanceIterator(Managed *m, ObjectIterator *it, String **name, uint *index, PropertyAttributes *attributes) -{ - return 0; -} diff --git a/src/qml/qml/v4/qv4regexp_p.h b/src/qml/qml/v4/qv4regexp_p.h deleted file mode 100644 index 6edbd4b2ad..0000000000 --- a/src/qml/qml/v4/qv4regexp_p.h +++ /dev/null @@ -1,151 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QV4REGEXP_H -#define QV4REGEXP_H - -#include -#include - -#include -#include -#include - -#include - -#include -#include -#include - -#include "qv4managed_p.h" -#include "qv4engine_p.h" - -QT_BEGIN_NAMESPACE - -namespace QV4 { - -struct ExecutionEngine; - -struct RegExpCacheKey -{ - RegExpCacheKey(const QString &pattern, bool ignoreCase, bool multiLine) - : pattern(pattern) - , ignoreCase(ignoreCase) - , multiLine(multiLine) - { } - explicit inline RegExpCacheKey(const RegExp *re); - - bool operator==(const RegExpCacheKey &other) const - { return pattern == other.pattern && ignoreCase == other.ignoreCase && multiLine == other.multiLine; } - bool operator!=(const RegExpCacheKey &other) const - { return !operator==(other); } - - QString pattern; - uint ignoreCase : 1; - uint multiLine : 1; -}; - -inline uint qHash(const RegExpCacheKey& key, uint seed = 0) Q_DECL_NOTHROW -{ return qHash(key.pattern, seed); } - -class RegExpCache : public QHash -{ -public: - ~RegExpCache(); -}; - -class RegExp : public Managed -{ - Q_MANAGED -public: - static RegExp* create(ExecutionEngine* engine, const QString& pattern, bool ignoreCase = false, bool multiline = false); - ~RegExp(); - - QString pattern() const { return m_pattern; } - - bool isValid() const { return m_byteCode.get(); } - - uint match(const QString& string, int start, uint *matchOffsets); - - bool ignoreCase() const { return m_ignoreCase; } - bool multiLine() const { return m_multiLine; } - int captureCount() const { return m_subPatternCount + 1; } - -protected: - static void destroy(Managed *that); - static void markObjects(Managed *that); - static Value get(Managed *m, String *name, bool *hasProperty); - static Value getIndexed(Managed *m, uint index, bool *hasProperty); - static void put(Managed *m, String *name, const Value &value); - static void putIndexed(Managed *m, uint index, const Value &value); - static PropertyAttributes query(const Managed *m, String *name); - static PropertyAttributes queryIndexed(const Managed *m, uint index); - static bool deleteProperty(Managed *, String *); - static bool deleteIndexedProperty(Managed *m, uint index); - static Property *advanceIterator(Managed *m, ObjectIterator *it, String **name, uint *index, PropertyAttributes *attributes); - -private: - friend class RegExpCache; - Q_DISABLE_COPY(RegExp); - RegExp(ExecutionEngine* engine, const QString& pattern, bool ignoreCase, bool multiline); - - const QString m_pattern; - OwnPtr m_byteCode; -#if ENABLE(YARR_JIT) - JSC::Yarr::YarrCodeBlock m_jitCode; -#endif - RegExpCache *m_cache; - int m_subPatternCount; - const bool m_ignoreCase; - const bool m_multiLine; -}; - -inline RegExpCacheKey::RegExpCacheKey(const RegExp *re) - : pattern(re->pattern()) - , ignoreCase(re->ignoreCase()) - , multiLine(re->multiLine()) -{} - - -} - -QT_END_NAMESPACE - -#endif // QV4REGEXP_H diff --git a/src/qml/qml/v4/qv4regexpobject.cpp b/src/qml/qml/v4/qv4regexpobject.cpp deleted file mode 100644 index 0dc14e5722..0000000000 --- a/src/qml/qml/v4/qv4regexpobject.cpp +++ /dev/null @@ -1,359 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4regexpobject_p.h" -#include "qv4jsir_p.h" -#include "qv4isel_p.h" -#include "qv4objectproto_p.h" -#include "qv4stringobject_p.h" -#include "qv4mm_p.h" - -#include -#include -#include -#include -#include -#include -#include "private/qlocale_tools_p.h" - -#include -#include -#include -#include -#include -#include -#include "qv4alloca_p.h" - -QT_BEGIN_NAMESPACE - -Q_CORE_EXPORT QString qt_regexp_toCanonical(const QString &, QRegExp::PatternSyntax); - -using namespace QV4; - -DEFINE_MANAGED_VTABLE(RegExpObject); - -RegExpObject::RegExpObject(ExecutionEngine *engine, RegExp* value, bool global) - : Object(engine) - , value(value) - , global(global) -{ - init(engine); -} - -// Converts a QRegExp to a JS RegExp. -// The conversion is not 100% exact since ECMA regexp and QRegExp -// have different semantics/flags, but we try to do our best. -RegExpObject::RegExpObject(ExecutionEngine *engine, const QRegExp &re) - : Object(engine) - , value(0) - , global(false) -{ - // Convert the pattern to a ECMAScript pattern. - QString pattern = QT_PREPEND_NAMESPACE(qt_regexp_toCanonical)(re.pattern(), re.patternSyntax()); - if (re.isMinimal()) { - QString ecmaPattern; - int len = pattern.length(); - ecmaPattern.reserve(len); - int i = 0; - const QChar *wc = pattern.unicode(); - bool inBracket = false; - while (i < len) { - QChar c = wc[i++]; - ecmaPattern += c; - switch (c.unicode()) { - case '?': - case '+': - case '*': - case '}': - if (!inBracket) - ecmaPattern += QLatin1Char('?'); - break; - case '\\': - if (i < len) - ecmaPattern += wc[i++]; - break; - case '[': - inBracket = true; - break; - case ']': - inBracket = false; - break; - default: - break; - } - } - pattern = ecmaPattern; - } - - value = RegExp::create(engine, pattern, re.caseSensitivity() == Qt::CaseInsensitive, false); - - init(engine); -} - -void RegExpObject::init(ExecutionEngine *engine) -{ - vtbl = &static_vtbl; - type = Type_RegExpObject; - - Property *lastIndexProperty = insertMember(engine->newIdentifier(QStringLiteral("lastIndex")), - Attr_NotEnumerable|Attr_NotConfigurable); - lastIndexProperty->value = Value::fromInt32(0); - if (!this->value) - return; - - QString p = this->value->pattern(); - if (p.isEmpty()) { - p = QStringLiteral("(?:)"); - } else { - // escape certain parts, see ch. 15.10.4 - p.replace('/', QLatin1String("\\/")); - } - - defineReadonlyProperty(engine->newIdentifier(QStringLiteral("source")), Value::fromString(engine->newString(p))); - defineReadonlyProperty(engine->newIdentifier(QStringLiteral("global")), Value::fromBoolean(global)); - defineReadonlyProperty(engine->newIdentifier(QStringLiteral("ignoreCase")), Value::fromBoolean(this->value->ignoreCase())); - defineReadonlyProperty(engine->newIdentifier(QStringLiteral("multiline")), Value::fromBoolean(this->value->multiLine())); -} - - -void RegExpObject::destroy(Managed *that) -{ - static_cast(that)->~RegExpObject(); -} - -void RegExpObject::markObjects(Managed *that) -{ - RegExpObject *re = static_cast(that); - if (re->value) - re->value->mark(); - Object::markObjects(that); -} - -Property *RegExpObject::lastIndexProperty(ExecutionContext *ctx) -{ - assert(0 == internalClass->find(ctx->engine->newIdentifier(QStringLiteral("lastIndex")))); - return &memberData[0]; -} - -// Converts a JS RegExp to a QRegExp. -// The conversion is not 100% exact since ECMA regexp and QRegExp -// have different semantics/flags, but we try to do our best. -QRegExp RegExpObject::toQRegExp() const -{ - Qt::CaseSensitivity caseSensitivity = value->ignoreCase() ? Qt::CaseInsensitive : Qt::CaseSensitive; - return QRegExp(value->pattern(), caseSensitivity, QRegExp::RegExp2); -} - -QString RegExpObject::toString() const -{ - QString result = QChar('/') + source(); - result += QChar('/'); - if (global) - result += QChar('g'); - if (value->ignoreCase()) - result += QChar('i'); - if (value->multiLine()) - result += QChar('m'); - return result; -} - -QString RegExpObject::source() const -{ - return const_cast(this)->get(internalClass->engine->newIdentifier(QStringLiteral("source"))).stringValue()->toQString(); -} - -uint RegExpObject::flags() const -{ - uint f = 0; - if (global) - f |= QV4::RegExpObject::RegExp_Global; - if (value->ignoreCase()) - f |= QV4::RegExpObject::RegExp_IgnoreCase; - if (value->multiLine()) - f |= QV4::RegExpObject::RegExp_Multiline; - return f; -} - -DEFINE_MANAGED_VTABLE(RegExpCtor); - -RegExpCtor::RegExpCtor(ExecutionContext *scope) - : FunctionObject(scope, scope->engine->newIdentifier(QStringLiteral("RegExp"))) -{ - vtbl = &static_vtbl; -} - -Value RegExpCtor::construct(Managed *m, Value *argv, int argc) -{ - Value r = argc > 0 ? argv[0] : Value::undefinedValue(); - Value f = argc > 1 ? argv[1] : Value::undefinedValue(); - ExecutionContext *ctx = m->engine()->current; - if (RegExpObject *re = r.as()) { - if (!f.isUndefined()) - ctx->throwTypeError(); - - RegExpObject *o = ctx->engine->newRegExpObject(re->value, re->global); - return Value::fromObject(o); - } - - QString pattern; - if (!r.isUndefined()) - pattern = r.toString(ctx)->toQString(); - - bool global = false; - bool ignoreCase = false; - bool multiLine = false; - if (!f.isUndefined()) { - f = __qmljs_to_string(f, ctx); - QString str = f.stringValue()->toQString(); - for (int i = 0; i < str.length(); ++i) { - if (str.at(i) == QChar('g') && !global) { - global = true; - } else if (str.at(i) == QChar('i') && !ignoreCase) { - ignoreCase = true; - } else if (str.at(i) == QChar('m') && !multiLine) { - multiLine = true; - } else { - ctx->throwSyntaxError(0); - } - } - } - - RegExp* re = RegExp::create(ctx->engine, pattern, ignoreCase, multiLine); - if (!re->isValid()) - ctx->throwSyntaxError(0); - - RegExpObject *o = ctx->engine->newRegExpObject(re, global); - return Value::fromObject(o); -} - -Value RegExpCtor::call(Managed *that, const Value &, Value *argv, int argc) -{ - if (argc > 0 && argv[0].as()) { - if (argc == 1 || argv[1].isUndefined()) - return argv[0]; - } - - return construct(that, argv, argc); -} - -void RegExpPrototype::init(ExecutionContext *ctx, const Value &ctor) -{ - ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); - ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(2)); - defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); - defineDefaultProperty(ctx, QStringLiteral("exec"), method_exec, 1); - defineDefaultProperty(ctx, QStringLiteral("test"), method_test, 1); - defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); - defineDefaultProperty(ctx, QStringLiteral("compile"), method_compile, 2); -} - -Value RegExpPrototype::method_exec(SimpleCallContext *ctx) -{ - RegExpObject *r = ctx->thisObject.as(); - if (!r) - ctx->throwTypeError(); - - Value arg = ctx->argument(0); - arg = __qmljs_to_string(arg, ctx); - QString s = arg.stringValue()->toQString(); - - int offset = r->global ? r->lastIndexProperty(ctx)->value.toInt32() : 0; - if (offset < 0 || offset > s.length()) { - r->lastIndexProperty(ctx)->value = Value::fromInt32(0); - return Value::nullValue(); - } - - uint* matchOffsets = (uint*)alloca(r->value->captureCount() * 2 * sizeof(uint)); - int result = r->value->match(s, offset, matchOffsets); - if (result == -1) { - r->lastIndexProperty(ctx)->value = Value::fromInt32(0); - return Value::nullValue(); - } - - // fill in result data - ArrayObject *array = ctx->engine->newArrayObject(); - for (int i = 0; i < r->value->captureCount(); ++i) { - int start = matchOffsets[i * 2]; - int end = matchOffsets[i * 2 + 1]; - Value entry = Value::undefinedValue(); - if (start != -1 && end != -1) - entry = Value::fromString(ctx, s.mid(start, end - start)); - array->push_back(entry); - } - - array->put(ctx, QLatin1String("index"), Value::fromInt32(result)); - array->put(ctx, QLatin1String("input"), arg); - - if (r->global) - r->lastIndexProperty(ctx)->value = Value::fromInt32(matchOffsets[1]); - - return Value::fromObject(array); -} - -Value RegExpPrototype::method_test(SimpleCallContext *ctx) -{ - Value r = method_exec(ctx); - return Value::fromBoolean(!r.isNull()); -} - -Value RegExpPrototype::method_toString(SimpleCallContext *ctx) -{ - RegExpObject *r = ctx->thisObject.as(); - if (!r) - ctx->throwTypeError(); - - return Value::fromString(ctx, r->toString()); -} - -Value RegExpPrototype::method_compile(SimpleCallContext *ctx) -{ - RegExpObject *r = ctx->thisObject.as(); - if (!r) - ctx->throwTypeError(); - - RegExpObject *re = ctx->engine->regExpCtor.asFunctionObject()->construct(ctx->arguments, ctx->argumentCount).as(); - - r->value = re->value; - r->global = re->global; - return Value::undefinedValue(); -} - -QT_END_NAMESPACE diff --git a/src/qml/qml/v4/qv4regexpobject_p.h b/src/qml/qml/v4/qv4regexpobject_p.h deleted file mode 100644 index 0b9b7122a9..0000000000 --- a/src/qml/qml/v4/qv4regexpobject_p.h +++ /dev/null @@ -1,123 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QV4REGEXPOBJECT_H -#define QV4REGEXPOBJECT_H - -#include "qv4runtime_p.h" -#include "qv4engine_p.h" -#include "qv4context_p.h" -#include "qv4functionobject_p.h" -#include "qv4string_p.h" -#include "qv4codegen_p.h" -#include "qv4isel_p.h" -#include "qv4managed_p.h" -#include "qv4property_p.h" -#include "qv4objectiterator_p.h" -#include "qv4regexp_p.h" - -#include -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE - -namespace QV4 { - -struct RegExp; - -struct RegExpObject: Object { - Q_MANAGED - // needs to be compatible with the flags in qv4jsir_p.h - enum Flags { - RegExp_Global = 0x01, - RegExp_IgnoreCase = 0x02, - RegExp_Multiline = 0x04 - }; - - RegExp* value; - Property *lastIndexProperty(ExecutionContext *ctx); - bool global; - RegExpObject(ExecutionEngine *engine, RegExp* value, bool global); - RegExpObject(ExecutionEngine *engine, const QRegExp &re); - ~RegExpObject() {} - - void init(ExecutionEngine *engine); - - QRegExp toQRegExp() const; - QString toString() const; - QString source() const; - uint flags() const; - -protected: - static void destroy(Managed *that); - static void markObjects(Managed *that); -}; - - -struct RegExpCtor: FunctionObject -{ - RegExpCtor(ExecutionContext *scope); - - static Value construct(Managed *m, Value *args, int argc); - static Value call(Managed *that, const Value &, Value *, int); - -protected: - static const ManagedVTable static_vtbl; -}; - -struct RegExpPrototype: RegExpObject -{ - RegExpPrototype(ExecutionEngine* engine): RegExpObject(engine, RegExp::create(engine, QString()), false) {} - void init(ExecutionContext *ctx, const Value &ctor); - - static Value method_exec(SimpleCallContext *ctx); - static Value method_test(SimpleCallContext *ctx); - static Value method_toString(SimpleCallContext *ctx); - static Value method_compile(SimpleCallContext *ctx); -}; - -} - -QT_END_NAMESPACE - -#endif // QMLJS_OBJECTS_H diff --git a/src/qml/qml/v4/qv4runtime.cpp b/src/qml/qml/v4/qv4runtime.cpp deleted file mode 100644 index ed2c1468ee..0000000000 --- a/src/qml/qml/v4/qv4runtime.cpp +++ /dev/null @@ -1,1250 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4global_p.h" -#include "qv4runtime_p.h" -#include "qv4object_p.h" -#include "qv4jsir_p.h" -#include "qv4objectproto_p.h" -#include "qv4globalobject_p.h" -#include "qv4stringobject_p.h" -#include "qv4lookup_p.h" -#include "qv4function_p.h" -#include "qv4exception_p.h" -#include "private/qlocale_tools_p.h" - -#include -#include -#include -#include -#include -#include -#include - -#include "../../../3rdparty/double-conversion/double-conversion.h" - -QT_BEGIN_NAMESPACE - -namespace QV4 { - -void __qmljs_numberToString(QString *result, double num, int radix) -{ - Q_ASSERT(result); - - if (std::isnan(num)) { - *result = QStringLiteral("NaN"); - return; - } else if (qIsInf(num)) { - *result = QLatin1String(num < 0 ? "-Infinity" : "Infinity"); - return; - } - - if (radix == 10) { - char str[100]; - double_conversion::StringBuilder builder(str, sizeof(str)); - double_conversion::DoubleToStringConverter::EcmaScriptConverter().ToShortest(num, &builder); - *result = QString::fromLatin1(builder.Finalize()); - return; - } - - result->clear(); - bool negative = false; - - if (num < 0) { - negative = true; - num = -num; - } - - double frac = num - ::floor(num); - num = Value::toInteger(num); - - do { - char c = (char)::fmod(num, radix); - c = (c < 10) ? (c + '0') : (c - 10 + 'a'); - result->prepend(QLatin1Char(c)); - num = ::floor(num / radix); - } while (num != 0); - - if (frac != 0) { - result->append(QLatin1Char('.')); - do { - frac = frac * radix; - char c = (char)::floor(frac); - c = (c < 10) ? (c + '0') : (c - 10 + 'a'); - result->append(QLatin1Char(c)); - frac = frac - ::floor(frac); - } while (frac != 0); - } - - if (negative) - result->prepend(QLatin1Char('-')); -} - -void __qmljs_init_closure(ExecutionContext *ctx, Value *result, Function *clos) -{ - assert(clos); - *result = Value::fromObject(ctx->engine->newScriptFunction(ctx, clos)); -} - -Function *__qmljs_register_function(ExecutionContext *ctx, String *name, - bool hasDirectEval, - bool usesArgumentsObject, bool isStrict, - bool hasNestedFunctions, - String **formals, unsigned formalCount, - String **locals, unsigned localCount) -{ - Function *f = ctx->engine->newFunction(name ? name->toQString() : QString()); - - f->hasDirectEval = hasDirectEval; - f->usesArgumentsObject = usesArgumentsObject; - f->isStrict = isStrict; - f->hasNestedFunctions = hasNestedFunctions; - - for (unsigned i = 0; i < formalCount; ++i) - if (formals[i]) - f->formals.append(formals[i]); - for (unsigned i = 0; i < localCount; ++i) - if (locals[i]) - f->locals.append(locals[i]); - - return f; -} - -void __qmljs_delete_subscript(ExecutionContext *ctx, Value *result, const Value &base, const Value &index) -{ - if (Object *o = base.asObject()) { - uint n = index.asArrayIndex(); - if (n < UINT_MAX) { - Value res = Value::fromBoolean(o->deleteIndexedProperty(n)); - if (result) - *result = res; - return; - } - } - - String *name = index.toString(ctx); - __qmljs_delete_member(ctx, result, base, name); -} - -void __qmljs_delete_member(ExecutionContext *ctx, Value *result, const Value &base, String *name) -{ - Object *obj = base.toObject(ctx); - Value res = Value::fromBoolean(obj->deleteProperty(name)); - if (result) - *result = res; -} - -void __qmljs_delete_name(ExecutionContext *ctx, Value *result, String *name) -{ - Value res = Value::fromBoolean(ctx->deleteProperty(name)); - if (result) - *result = res; -} - -void __qmljs_add_helper(ExecutionContext *ctx, Value *result, const Value &left, const Value &right) -{ - Value pleft = __qmljs_to_primitive(left, PREFERREDTYPE_HINT); - Value pright = __qmljs_to_primitive(right, PREFERREDTYPE_HINT); - if (pleft.isString() || pright.isString()) { - if (!pleft.isString()) - pleft = __qmljs_to_string(pleft, ctx); - if (!pright.isString()) - pright = __qmljs_to_string(pright, ctx); - String *string = __qmljs_string_concat(ctx, pleft.stringValue(), pright.stringValue()); - *result = Value::fromString(string); - return; - } - double x = __qmljs_to_number(pleft); - double y = __qmljs_to_number(pright); - *result = Value::fromDouble(x + y); -} - -void __qmljs_instanceof(ExecutionContext *ctx, Value *result, const Value &left, const Value &right) -{ - Object *o = right.asObject(); - if (!o) - ctx->throwTypeError(); - - bool r = o->hasInstance(left); - *result = Value::fromBoolean(r); -} - -void __qmljs_in(ExecutionContext *ctx, Value *result, const Value &left, const Value &right) -{ - if (!right.isObject()) - ctx->throwTypeError(); - String *s = left.toString(ctx); - bool r = right.objectValue()->__hasProperty__(s); - *result = Value::fromBoolean(r); -} - -void __qmljs_inplace_bit_and_name(ExecutionContext *ctx, String *name, const Value &value) -{ - ctx->inplaceBitOp(name, value, __qmljs_bit_and); -} - -void __qmljs_inplace_bit_or_name(ExecutionContext *ctx, String *name, const Value &value) -{ - ctx->inplaceBitOp(name, value, __qmljs_bit_or); -} - -void __qmljs_inplace_bit_xor_name(ExecutionContext *ctx, String *name, const Value &value) -{ - ctx->inplaceBitOp(name, value, __qmljs_bit_xor); -} - -void __qmljs_inplace_add_name(ExecutionContext *ctx, String *name, const Value &value) -{ - Value lhs = ctx->getProperty(name); - Value result; - __qmljs_add(ctx, &result, lhs, value); - ctx->setProperty(name, result); -} - -void __qmljs_inplace_sub_name(ExecutionContext *ctx, String *name, const Value &value) -{ - ctx->inplaceBitOp(name, value, __qmljs_sub); -} - -void __qmljs_inplace_mul_name(ExecutionContext *ctx, String *name, const Value &value) -{ - ctx->inplaceBitOp(name, value, __qmljs_mul); -} - -void __qmljs_inplace_div_name(ExecutionContext *ctx, String *name, const Value &value) -{ - ctx->inplaceBitOp(name, value, __qmljs_div); -} - -void __qmljs_inplace_mod_name(ExecutionContext *ctx, String *name, const Value &value) -{ - ctx->inplaceBitOp(name, value, __qmljs_mod); -} - -void __qmljs_inplace_shl_name(ExecutionContext *ctx, String *name, const Value &value) -{ - ctx->inplaceBitOp(name, value, __qmljs_shl); -} - -void __qmljs_inplace_shr_name(ExecutionContext *ctx, String *name, const Value &value) -{ - ctx->inplaceBitOp(name, value, __qmljs_shr); -} - -void __qmljs_inplace_ushr_name(ExecutionContext *ctx, String *name, const Value &value) -{ - ctx->inplaceBitOp(name, value, __qmljs_ushr); -} - -void __qmljs_inplace_bit_and_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs) -{ - Object *obj = base.toObject(ctx); - obj->inplaceBinOp(ctx, __qmljs_bit_and, index, rhs); -} - -void __qmljs_inplace_bit_or_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs) -{ - Object *obj = base.toObject(ctx); - obj->inplaceBinOp(ctx, __qmljs_bit_or, index, rhs); -} - -void __qmljs_inplace_bit_xor_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs) -{ - Object *obj = base.toObject(ctx); - obj->inplaceBinOp(ctx, __qmljs_bit_xor, index, rhs); -} - -void __qmljs_inplace_add_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs) -{ - Object *obj = base.toObject(ctx); - obj->inplaceBinOp(ctx, __qmljs_add, index, rhs); -} - -void __qmljs_inplace_sub_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs) -{ - Object *obj = base.toObject(ctx); - obj->inplaceBinOp(ctx, __qmljs_sub, index, rhs); -} - -void __qmljs_inplace_mul_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs) -{ - Object *obj = base.toObject(ctx); - obj->inplaceBinOp(ctx, __qmljs_mul, index, rhs); -} - -void __qmljs_inplace_div_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs) -{ - Object *obj = base.toObject(ctx); - obj->inplaceBinOp(ctx, __qmljs_div, index, rhs); -} - -void __qmljs_inplace_mod_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs) -{ - Object *obj = base.toObject(ctx); - obj->inplaceBinOp(ctx, __qmljs_mod, index, rhs); -} - -void __qmljs_inplace_shl_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs) -{ - Object *obj = base.toObject(ctx); - obj->inplaceBinOp(ctx, __qmljs_shl, index, rhs); -} - -void __qmljs_inplace_shr_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs) -{ - Object *obj = base.toObject(ctx); - obj->inplaceBinOp(ctx, __qmljs_shr, index, rhs); -} - -void __qmljs_inplace_ushr_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs) -{ - Object *obj = base.toObject(ctx); - obj->inplaceBinOp(ctx, __qmljs_ushr, index, rhs); -} - -void __qmljs_inplace_bit_and_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs) -{ - Object *o = base.toObject(ctx); - o->inplaceBinOp(ctx, __qmljs_bit_and, name, rhs); -} - -void __qmljs_inplace_bit_or_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs) -{ - Object *o = base.toObject(ctx); - o->inplaceBinOp(ctx, __qmljs_bit_or, name, rhs); -} - -void __qmljs_inplace_bit_xor_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs) -{ - Object *o = base.toObject(ctx); - o->inplaceBinOp(ctx, __qmljs_bit_xor, name, rhs); -} - -void __qmljs_inplace_add_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs) -{ - Object *o = base.toObject(ctx); - o->inplaceBinOp(ctx, __qmljs_add, name, rhs); -} - -void __qmljs_inplace_sub_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs) -{ - Object *o = base.toObject(ctx); - o->inplaceBinOp(ctx, __qmljs_sub, name, rhs); -} - -void __qmljs_inplace_mul_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs) -{ - Object *o = base.toObject(ctx); - o->inplaceBinOp(ctx, __qmljs_mul, name, rhs); -} - -void __qmljs_inplace_div_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs) -{ - Object *o = base.toObject(ctx); - o->inplaceBinOp(ctx, __qmljs_div, name, rhs); -} - -void __qmljs_inplace_mod_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs) -{ - Object *o = base.toObject(ctx); - o->inplaceBinOp(ctx, __qmljs_mod, name, rhs); -} - -void __qmljs_inplace_shl_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs) -{ - Object *o = base.toObject(ctx); - o->inplaceBinOp(ctx, __qmljs_shl, name, rhs); -} - -void __qmljs_inplace_shr_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs) -{ - Object *o = base.toObject(ctx); - o->inplaceBinOp(ctx, __qmljs_shr, name, rhs); -} - -void __qmljs_inplace_ushr_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs) -{ - Object *o = base.toObject(ctx); - o->inplaceBinOp(ctx, __qmljs_ushr, name, rhs); -} - -double __qmljs_string_to_number(const QString &string) -{ - QString s = string.trimmed(); - if (s.startsWith(QLatin1String("0x")) || s.startsWith(QLatin1String("0X"))) - return s.toLong(0, 16); - bool ok; - QByteArray ba = s.toLatin1(); - const char *begin = ba.constData(); - const char *end = 0; - double d = qstrtod(begin, &end, &ok); - if (end - begin != ba.size()) { - if (ba == "Infinity" || ba == "+Infinity") - d = Q_INFINITY; - else if (ba == "-Infinity") - d = -Q_INFINITY; - else - d = std::numeric_limits::quiet_NaN(); - } - return d; -} - -Value __qmljs_string_from_number(ExecutionContext *ctx, double number) -{ - QString qstr; - __qmljs_numberToString(&qstr, number, 10); - String *string = ctx->engine->newString(qstr); - return Value::fromString(string); -} - -String *__qmljs_string_concat(ExecutionContext *ctx, String *first, String *second) -{ - const QString &a = first->toQString(); - const QString &b = second->toQString(); - QString newStr(a.length() + b.length(), Qt::Uninitialized); - QChar *data = newStr.data(); - memcpy(data, a.constData(), a.length()*sizeof(QChar)); - data += a.length(); - memcpy(data, b.constData(), b.length()*sizeof(QChar)); - - return ctx->engine->newString(newStr); -} - -Value __qmljs_object_default_value(Object *object, int typeHint) -{ - if (typeHint == PREFERREDTYPE_HINT) { - if (object->asDateObject()) - typeHint = STRING_HINT; - else - typeHint = NUMBER_HINT; - } - - ExecutionEngine *engine = object->internalClass->engine; - String *meth1 = engine->newString("toString"); - String *meth2 = engine->newString("valueOf"); - - if (typeHint == NUMBER_HINT) - qSwap(meth1, meth2); - - ExecutionContext *ctx = engine->current; - - Value conv = object->get(meth1); - if (FunctionObject *o = conv.asFunctionObject()) { - Value r = o->call(Value::fromObject(object), 0, 0); - if (r.isPrimitive()) - return r; - } - - conv = object->get(meth2); - if (FunctionObject *o = conv.asFunctionObject()) { - Value r = o->call(Value::fromObject(object), 0, 0); - if (r.isPrimitive()) - return r; - } - - ctx->throwTypeError(); - return Value::undefinedValue(); -} - -Bool __qmljs_to_boolean(const Value &value) -{ - return value.toBoolean(); -} - - -Object *__qmljs_convert_to_object(ExecutionContext *ctx, const Value &value) -{ - assert(!value.isObject()); - switch (value.type()) { - case Value::Undefined_Type: - case Value::Null_Type: - ctx->throwTypeError(); - case Value::Boolean_Type: - return ctx->engine->newBooleanObject(value); - case Value::String_Type: - return ctx->engine->newStringObject(value); - break; - case Value::Object_Type: - Q_UNREACHABLE(); - case Value::Integer_Type: - default: // double - return ctx->engine->newNumberObject(value); - } -} - -String *__qmljs_convert_to_string(ExecutionContext *ctx, const Value &value) -{ - switch (value.type()) { - case Value::Undefined_Type: - return ctx->engine->id_undefined; - case Value::Null_Type: - return ctx->engine->id_null; - case Value::Boolean_Type: - if (value.booleanValue()) - return ctx->engine->id_true; - else - return ctx->engine->id_false; - case Value::String_Type: - return value.stringValue(); - case Value::Object_Type: { - Value prim = __qmljs_to_primitive(value, STRING_HINT); - if (prim.isPrimitive()) - return __qmljs_convert_to_string(ctx, prim); - else - ctx->throwTypeError(); - } - case Value::Integer_Type: - return __qmljs_string_from_number(ctx, value.int_32).stringValue(); - default: // double - return __qmljs_string_from_number(ctx, value.doubleValue()).stringValue(); - } // switch -} - -void __qmljs_set_property(ExecutionContext *ctx, const Value &object, String *name, const Value &value) -{ - Object *o = object.toObject(ctx); - o->put(name, value); -} - -void __qmljs_get_element(ExecutionContext *ctx, Value *result, const Value &object, const Value &index) -{ - uint idx = index.asArrayIndex(); - - Object *o = object.asObject(); - if (!o) { - if (idx < UINT_MAX) { - if (String *str = object.asString()) { - if (idx >= (uint)str->toQString().length()) { - if (result) - *result = Value::undefinedValue(); - return; - } - const QString s = str->toQString().mid(idx, 1); - if (result) - *result = Value::fromString(ctx, s); - return; - } - } - - if (object.isNull() || object.isUndefined()) { - QString message = QStringLiteral("Cannot read property '%1' of %2").arg(index.toQString()).arg(object.toQString()); - ctx->throwTypeError(message); - } - - o = __qmljs_convert_to_object(ctx, object); - } - - if (idx < UINT_MAX) { - uint pidx = o->propertyIndexFromArrayIndex(idx); - if (pidx < UINT_MAX) { - if (!o->arrayAttributes || o->arrayAttributes[pidx].isData()) { - if (result) - *result = o->arrayData[pidx].value; - return; - } - } - - Value res = o->getIndexed(idx); - if (result) - *result = res; - return; - } - - String *name = index.toString(ctx); - Value res = o->get(name); - if (result) - *result = res; -} - -void __qmljs_set_element(ExecutionContext *ctx, const Value &object, const Value &index, const Value &value) -{ - Object *o = object.toObject(ctx); - - uint idx = index.asArrayIndex(); - if (idx < UINT_MAX) { - uint pidx = o->propertyIndexFromArrayIndex(idx); - if (pidx < UINT_MAX) { - if (o->arrayAttributes && !o->arrayAttributes[pidx].isEmpty() && !o->arrayAttributes[pidx].isWritable()) { - if (ctx->strictMode) - ctx->throwTypeError(); - return; - } - - Property *p = o->arrayData + pidx; - if (!o->arrayAttributes || o->arrayAttributes[pidx].isData()) { - p->value = value; - return; - } - - if (o->arrayAttributes[pidx].isAccessor()) { - FunctionObject *setter = p->setter(); - if (!setter) { - if (ctx->strictMode) - ctx->throwTypeError(); - return; - } - - Value args[1]; - args[0] = value; - setter->call(Value::fromObject(o), args, 1); - return; - } - } - o->putIndexed(idx, value); - return; - } - - String *name = index.toString(ctx); - o->put(name, value); -} - -void __qmljs_foreach_iterator_object(ExecutionContext *ctx, Value *result, const Value &in) -{ - Object *o = 0; - if (!in.isNull() && !in.isUndefined()) - o = in.toObject(ctx); - Object *it = ctx->engine->newForEachIteratorObject(ctx, o); - *result = Value::fromObject(it); -} - -void __qmljs_foreach_next_property_name(Value *result, const Value &foreach_iterator) -{ - assert(foreach_iterator.isObject()); - - ForEachIteratorObject *it = static_cast(foreach_iterator.objectValue()); - assert(it->as()); - - *result = it->nextPropertyName(); -} - - -void __qmljs_set_activation_property(ExecutionContext *ctx, String *name, const Value &value) -{ - ctx->setProperty(name, value); -} - -void __qmljs_get_property(ExecutionContext *ctx, Value *result, const Value &object, String *name) -{ - Value res; - Managed *m = object.asManaged(); - if (m) { - res = m->get(name); - } else { - if (object.isNull() || object.isUndefined()) { - QString message = QStringLiteral("Cannot read property '%1' of %2").arg(name->toQString()).arg(object.toQString()); - ctx->throwTypeError(message); - } - - m = __qmljs_convert_to_object(ctx, object); - res = m->get(name); - } - if (result) - *result = res; -} - -void __qmljs_get_activation_property(ExecutionContext *ctx, Value *result, String *name) -{ - *result = ctx->getProperty(name); -} - -uint __qmljs_equal_helper(const Value &x, const Value &y) -{ - Q_ASSERT(x.type() != y.type()); - - if (x.isNumber() && y.isNumber()) - return x.asDouble() == y.asDouble(); - if (x.isNull() && y.isUndefined()) { - return true; - } else if (x.isUndefined() && y.isNull()) { - return true; - } else if (x.isNumber() && y.isString()) { - double dy = __qmljs_to_number(y); - return x.asDouble() == dy; - } else if (x.isString() && y.isNumber()) { - double dx = __qmljs_to_number(x); - return dx == y.asDouble(); - } else if (x.isBoolean()) { - Value nx = Value::fromDouble((double) x.booleanValue()); - return __qmljs_cmp_eq(nx, y); - } else if (y.isBoolean()) { - Value ny = Value::fromDouble((double) y.booleanValue()); - return __qmljs_cmp_eq(x, ny); - } else if ((x.isNumber() || x.isString()) && y.isObject()) { - Value py = __qmljs_to_primitive(y, PREFERREDTYPE_HINT); - return __qmljs_cmp_eq(x, py); - } else if (x.isObject() && (y.isNumber() || y.isString())) { - Value px = __qmljs_to_primitive(x, PREFERREDTYPE_HINT); - return __qmljs_cmp_eq(px, y); - } - - return false; -} - -Bool __qmljs_strict_equal(const Value &x, const Value &y) -{ - TRACE2(x, y); - - if (x.rawValue() == y.rawValue()) - // NaN != NaN - return (x.tag & QV4::Value::NotDouble_Mask) != QV4::Value::NaN_Mask; - - if (x.isNumber()) - return y.isNumber() && x.asDouble() == y.asDouble(); - if (x.isString()) - return y.isString() && x.stringValue()->isEqualTo(y.stringValue()); - return false; -} - - -void __qmljs_call_global_lookup(ExecutionContext *context, Value *result, uint index, Value *args, int argc) -{ - Lookup *l = context->lookups + index; - Value v; - l->globalGetter(l, context, &v); - FunctionObject *o = v.asFunctionObject(); - if (!o) - context->throwTypeError(); - - Value thisObject = Value::undefinedValue(); - - if (o == context->engine->evalFunction && l->name->isEqualTo(context->engine->id_eval)) { - Value res = static_cast(o)->evalCall(thisObject, args, argc, true); - if (result) - *result = res; - return; - } - - Value res = o->call(thisObject, args, argc); - if (result) - *result = res; -} - - -void __qmljs_call_activation_property(ExecutionContext *context, Value *result, String *name, Value *args, int argc) -{ - Object *base; - Value func = context->getPropertyAndBase(name, &base); - FunctionObject *o = func.asFunctionObject(); - if (!o) { - QString objectAsString = QStringLiteral("[null]"); - if (base) - objectAsString = Value::fromObject(base).toQString(); - QString msg = QStringLiteral("Property '%1' of object %2 is not a function").arg(name->toQString()).arg(objectAsString); - context->throwTypeError(msg); - } - - Value thisObject = base ? Value::fromObject(base) : Value::undefinedValue(); - - if (o == context->engine->evalFunction && name->isEqualTo(context->engine->id_eval)) { - Value res = static_cast(o)->evalCall(thisObject, args, argc, true); - if (result) - *result = res; - return; - } - - Value res = o->call(thisObject, args, argc); - if (result) - *result = res; -} - -void __qmljs_call_property(ExecutionContext *context, Value *result, const Value &thatObject, String *name, Value *args, int argc) -{ - Value thisObject = thatObject; - Managed *baseObject = thisObject.asManaged(); - if (!baseObject) { - if (thisObject.isNull() || thisObject.isUndefined()) { - QString message = QStringLiteral("Cannot call method '%1' of %2").arg(name->toQString()).arg(thisObject.toQString()); - context->throwTypeError(message); - } - - baseObject = __qmljs_convert_to_object(context, thisObject); - thisObject = Value::fromObject(static_cast(baseObject)); - } - - Value func = baseObject->get(name); - FunctionObject *o = func.asFunctionObject(); - if (!o) { - QString error = QString("Property '%1' of object %2 is not a function").arg(name->toQString(), thisObject.toQString()); - context->throwTypeError(error); - } - - Value res = o->call(thisObject, args, argc); - if (result) - *result = res; -} - -void __qmljs_call_property_lookup(ExecutionContext *context, Value *result, const Value &thisObject, uint index, Value *args, int argc) -{ - Lookup *l = context->lookups + index; - - Object *baseObject; - if (thisObject.isObject()) - baseObject = thisObject.objectValue(); - else if (thisObject.isString()) - baseObject = context->engine->stringPrototype; - else - baseObject = __qmljs_convert_to_object(context, thisObject); - - PropertyAttributes attrs; - Property *p = l->lookup(baseObject, &attrs); - if (!p) - context->throwTypeError(); - Value func = attrs.isData() ? p->value : baseObject->getValue(p, attrs); - FunctionObject *o = func.asFunctionObject(); - if (!o) - context->throwTypeError(); - - Value res = o->call(thisObject, args, argc); - if (result) - *result = res; -} - -void __qmljs_call_element(ExecutionContext *context, Value *result, const Value &that, const Value &index, Value *args, int argc) -{ - Object *baseObject = that.toObject(context); - Value thisObject = Value::fromObject(baseObject); - - Value func = baseObject->get(index.toString(context)); - Object *o = func.asObject(); - if (!o) - context->throwTypeError(); - - Value res = o->call(thisObject, args, argc); - if (result) - *result = res; -} - -void __qmljs_call_value(ExecutionContext *context, Value *result, const Value *thisObject, const Value &func, Value *args, int argc) -{ - Object *o = func.asObject(); - if (!o) - context->throwTypeError(); - Value res = o->call(thisObject ? *thisObject : Value::undefinedValue(), args, argc); - if (result) - *result = res; -} - - -void __qmljs_construct_global_lookup(ExecutionContext *context, Value *result, uint index, Value *args, int argc) -{ - Lookup *l = context->lookups + index; - Value func; - l->globalGetter(l, context, &func); - - if (Object *f = func.asObject()) { - Value res = f->construct(args, argc); - if (result) - *result = res; - return; - } - - context->throwTypeError(); -} - - -void __qmljs_construct_activation_property(ExecutionContext *context, Value *result, String *name, Value *args, int argc) -{ - Value func = context->getProperty(name); - __qmljs_construct_value(context, result, func, args, argc); -} - -void __qmljs_construct_value(ExecutionContext *context, Value *result, const Value &func, Value *args, int argc) -{ - if (Object *f = func.asObject()) { - Value res = f->construct(args, argc); - if (result) - *result = res; - return; - } - - context->throwTypeError(); -} - -void __qmljs_construct_property(ExecutionContext *context, Value *result, const Value &base, String *name, Value *args, int argc) -{ - Object *thisObject = base.toObject(context); - - Value func = thisObject->get(name); - if (Object *f = func.asObject()) { - Value res = f->construct(args, argc); - if (result) - *result = res; - return; - } - - context->throwTypeError(); -} - -void __qmljs_throw(ExecutionContext *context, const Value &value) -{ - Exception::throwException(context, value); -} - -void __qmljs_builtin_typeof(ExecutionContext *ctx, Value *result, const Value &value) -{ - if (!result) - return; - String *res = 0; - switch (value.type()) { - case Value::Undefined_Type: - res = ctx->engine->id_undefined; - break; - case Value::Null_Type: - res = ctx->engine->id_object; - break; - case Value::Boolean_Type: - res = ctx->engine->id_boolean; - break; - case Value::String_Type: - res = ctx->engine->id_string; - break; - case Value::Object_Type: - if (value.objectValue()->asFunctionObject()) - res = ctx->engine->id_function; - else - res = ctx->engine->id_object; // ### implementation-defined - break; - default: - res = ctx->engine->id_number; - break; - } - *result = Value::fromString(res); -} - -void __qmljs_builtin_typeof_name(ExecutionContext *context, Value *result, String *name) -{ - Value res; - __qmljs_builtin_typeof(context, &res, context->getPropertyNoThrow(name)); - if (result) - *result = res; -} - -void __qmljs_builtin_typeof_member(ExecutionContext *context, Value *result, const Value &base, String *name) -{ - Object *obj = base.toObject(context); - Value res; - __qmljs_builtin_typeof(context, &res, obj->get(name)); - if (result) - *result = res; -} - -void __qmljs_builtin_typeof_element(ExecutionContext *context, Value *result, const Value &base, const Value &index) -{ - String *name = index.toString(context); - Object *obj = base.toObject(context); - Value res; - __qmljs_builtin_typeof(context, &res, obj->get(name)); - if (result) - *result = res; -} - -void __qmljs_builtin_post_increment(Value *result, Value *val) -{ - if (val->isInteger() && val->integerValue() < INT_MAX) { - if (result) - *result = *val; - val->int_32 += 1; - return; - } - - double d = __qmljs_to_number(*val); - *val = Value::fromDouble(d + 1); - if (result) - *result = Value::fromDouble(d); -} - -void __qmljs_builtin_post_increment_name(ExecutionContext *context, Value *result, String *name) -{ - Value v = context->getProperty(name); - - if (v.isInteger() && v.integerValue() < INT_MAX) { - if (result) - *result = v; - v.int_32 += 1; - } else { - double d = __qmljs_to_number(v); - if (result) - *result = Value::fromDouble(d); - v = Value::fromDouble(d + 1); - } - - context->setProperty(name, v); -} - -void __qmljs_builtin_post_increment_member(ExecutionContext *context, Value *result, const Value &base, String *name) -{ - Object *o = base.toObject(context); - - Value v = o->get(name); - - if (v.isInteger() && v.integerValue() < INT_MAX) { - if (result) - *result = v; - v.int_32 += 1; - } else { - double d = __qmljs_to_number(v); - if (result) - *result = Value::fromDouble(d); - v = Value::fromDouble(d + 1); - } - - o->put(name, v); -} - -void __qmljs_builtin_post_increment_element(ExecutionContext *context, Value *result, const Value &base, const Value *index) -{ - Object *o = base.toObject(context); - - uint idx = index->asArrayIndex(); - - if (idx == UINT_MAX) { - String *s = index->toString(context); - return __qmljs_builtin_post_increment_member(context, result, base, s); - } - - Value v = o->getIndexed(idx); - - if (v.isInteger() && v.integerValue() < INT_MAX) { - if (result) - *result = v; - v.int_32 += 1; - } else { - double d = __qmljs_to_number(v); - if (result) - *result = Value::fromDouble(d); - v = Value::fromDouble(d + 1); - } - - o->putIndexed(idx, v); -} - -void __qmljs_builtin_post_decrement(Value *result, Value *val) -{ - if (val->isInteger() && val->integerValue() > INT_MIN) { - if (result) - *result = *val; - val->int_32 -= 1; - return; - } - - double d = __qmljs_to_number(*val); - *val = Value::fromDouble(d - 1); - if (result) - *result = Value::fromDouble(d); -} - -void __qmljs_builtin_post_decrement_name(ExecutionContext *context, Value *result, String *name) -{ - Value v = context->getProperty(name); - - if (v.isInteger() && v.integerValue() > INT_MIN) { - if (result) - *result = v; - v.int_32 -= 1; - } else { - double d = __qmljs_to_number(v); - if (result) - *result = Value::fromDouble(d); - v = Value::fromDouble(d - 1); - } - - context->setProperty(name, v); -} - -void __qmljs_builtin_post_decrement_member(ExecutionContext *context, Value *result, const Value &base, String *name) -{ - Object *o = base.toObject(context); - - Value v = o->get(name); - - if (v.isInteger() && v.integerValue() > INT_MIN) { - if (result) - *result = v; - v.int_32 -= 1; - } else { - double d = __qmljs_to_number(v); - if (result) - *result = Value::fromDouble(d); - v = Value::fromDouble(d - 1); - } - - o->put(name, v); -} - -void __qmljs_builtin_post_decrement_element(ExecutionContext *context, Value *result, const Value &base, const Value &index) -{ - Object *o = base.toObject(context); - - uint idx = index.asArrayIndex(); - - if (idx == UINT_MAX) { - String *s = index.toString(context); - return __qmljs_builtin_post_decrement_member(context, result, base, s); - } - - Value v = o->getIndexed(idx); - - if (v.isInteger() && v.integerValue() > INT_MIN) { - if (result) - *result = v; - v.int_32 -= 1; - } else { - double d = __qmljs_to_number(v); - if (result) - *result = Value::fromDouble(d); - v = Value::fromDouble(d - 1); - } - - o->putIndexed(idx, v); -} - -ExecutionContext *__qmljs_builtin_push_with_scope(const Value &o, ExecutionContext *ctx) -{ - Object *obj = o.toObject(ctx); - return ctx->engine->newWithContext(obj); -} - -ExecutionContext *__qmljs_builtin_push_catch_scope(String *exceptionVarName, const Value &exceptionValue, ExecutionContext *ctx) -{ - return ctx->engine->newCatchContext(exceptionVarName, exceptionValue); -} - -ExecutionContext *__qmljs_builtin_pop_scope(ExecutionContext *ctx) -{ - return ctx->engine->popContext(); -} - -void __qmljs_builtin_declare_var(ExecutionContext *ctx, bool deletable, String *name) -{ - ctx->createMutableBinding(name, deletable); -} - -void __qmljs_builtin_define_property(ExecutionContext *ctx, const Value &object, String *name, Value *val) -{ - Object *o = object.asObject(); - assert(o); - - uint idx = name->asArrayIndex(); - Property *pd = (idx != UINT_MAX) ? o->arrayInsert(idx) : o->insertMember(name, Attr_Data); - pd->value = val ? *val : Value::undefinedValue(); -} - -void __qmljs_builtin_define_array(ExecutionContext *ctx, Value *array, Value *values, uint length) -{ - ArrayObject *a = ctx->engine->newArrayObject(); - - // ### FIXME: We need to allocate the array data to avoid crashes other places - // This should rather be done when required - a->arrayReserve(length); - if (length) { - a->arrayDataLen = length; - Property *pd = a->arrayData; - for (uint i = 0; i < length; ++i) { - if (values[i].isEmpty()) { - a->ensureArrayAttributes(); - pd->value = Value::undefinedValue(); - a->arrayAttributes[i].clear(); - } else { - pd->value = values[i]; - } - ++pd; - } - a->setArrayLengthUnchecked(length); - } - *array = Value::fromObject(a); -} - -void __qmljs_builtin_define_getter_setter(ExecutionContext *ctx, const Value &object, String *name, const Value *getter, const Value *setter) -{ - Object *o = object.asObject(); - assert(o); - - uint idx = name->asArrayIndex(); - Property *pd = (idx != UINT_MAX) ? o->arrayInsert(idx, Attr_Accessor) : o->insertMember(name, Attr_Accessor); - pd->setGetter(getter ? getter->asFunctionObject() : 0); - pd->setSetter(setter ? setter->asFunctionObject() : 0); -} - -void __qmljs_builtin_define_object_literal(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value *args, QV4::InternalClass *klass) -{ - Object *o = ctx->engine->newObject(klass); - - for (int i = 0; i < klass->size; ++i) { - if (klass->propertyData[i].isData()) - o->memberData[i].value = *args++; - else { - o->memberData[i].setGetter(args->asFunctionObject()); - args++; - o->memberData[i].setSetter(args->asFunctionObject()); - args++; - } - } - - *result = Value::fromObject(o); -} - -void __qmljs_increment(Value *result, const Value &value) -{ - TRACE1(value); - - if (value.isInteger()) - *result = Value::fromInt32(value.integerValue() + 1); - else { - double d = __qmljs_to_number(value); - *result = Value::fromDouble(d + 1); - } -} - -void __qmljs_decrement(Value *result, const Value &value) -{ - TRACE1(value); - - if (value.isInteger()) - *result = Value::fromInt32(value.integerValue() - 1); - else { - double d = __qmljs_to_number(value); - *result = Value::fromDouble(d - 1); - } -} - -} // namespace QV4 - -QT_END_NAMESPACE diff --git a/src/qml/qml/v4/qv4runtime_p.h b/src/qml/qml/v4/qv4runtime_p.h deleted file mode 100644 index 836aedf298..0000000000 --- a/src/qml/qml/v4/qv4runtime_p.h +++ /dev/null @@ -1,722 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QMLJS_RUNTIME_H -#define QMLJS_RUNTIME_H - -#include "qv4global_p.h" -#include "qv4value_p.h" -#include "qv4math_p.h" - - -#include -#include -#include -#include - -#include -#include -#include - -//#include - -#ifdef DO_TRACE_INSTR -# define TRACE1(x) fprintf(stderr, " %s\n", __FUNCTION__); -# define TRACE2(x, y) fprintf(stderr, " %s\n", __FUNCTION__); -#else -# define TRACE1(x) -# define TRACE2(x, y) -#endif // TRACE1 - -QT_BEGIN_NAMESPACE - -namespace QV4 { - -enum TypeHint { - PREFERREDTYPE_HINT, - NUMBER_HINT, - STRING_HINT -}; - -struct Function; -struct Object; -struct String; -struct ExecutionContext; -struct FunctionObject; -struct BooleanObject; -struct NumberObject; -struct StringObject; -struct DateObject; -struct RegExpObject; -struct ArrayObject; -struct ErrorObject; -struct ExecutionEngine; -struct InternalClass; - -// context -void __qmljs_call_activation_property(QV4::ExecutionContext *, QV4::Value *result, QV4::String *name, QV4::Value *args, int argc); -void __qmljs_call_property(QV4::ExecutionContext *context, QV4::Value *result, const QV4::Value &that, QV4::String *name, QV4::Value *args, int argc); -void __qmljs_call_property_lookup(QV4::ExecutionContext *context, QV4::Value *result, const QV4::Value &thisObject, uint index, QV4::Value *args, int argc); -void __qmljs_call_element(QV4::ExecutionContext *context, QV4::Value *result, const QV4::Value &that, const QV4::Value &index, QV4::Value *args, int argc); -void __qmljs_call_value(QV4::ExecutionContext *context, QV4::Value *result, const QV4::Value *thisObject, const QV4::Value &func, QV4::Value *args, int argc); - -void __qmljs_construct_activation_property(QV4::ExecutionContext *, QV4::Value *result, QV4::String *name, QV4::Value *args, int argc); -void __qmljs_construct_property(QV4::ExecutionContext *context, QV4::Value *result, const QV4::Value &base, QV4::String *name, QV4::Value *args, int argc); -void __qmljs_construct_value(QV4::ExecutionContext *context, QV4::Value *result, const QV4::Value &func, QV4::Value *args, int argc); - -void __qmljs_builtin_typeof(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value &val); -void __qmljs_builtin_typeof_name(QV4::ExecutionContext *context, QV4::Value* result, QV4::String *name); -void __qmljs_builtin_typeof_member(QV4::ExecutionContext* context, QV4::Value* result, const QV4::Value &base, QV4::String *name); -void __qmljs_builtin_typeof_element(QV4::ExecutionContext* context, QV4::Value *result, const QV4::Value &base, const QV4::Value &index); - -void __qmljs_builtin_post_increment(QV4::Value *result, QV4::Value *val); -void __qmljs_builtin_post_increment_name(QV4::ExecutionContext *context, QV4::Value *result, QV4::String *name); -void __qmljs_builtin_post_increment_member(QV4::ExecutionContext *context, QV4::Value *result, const QV4::Value &base, QV4::String *name); -void __qmljs_builtin_post_increment_element(QV4::ExecutionContext *context, QV4::Value *result, const QV4::Value &base, const QV4::Value *index); - -void __qmljs_builtin_post_decrement(QV4::Value *result, QV4::Value *val); -void __qmljs_builtin_post_decrement_name(QV4::ExecutionContext *context, QV4::Value *result, QV4::String *name); -void __qmljs_builtin_post_decrement_member(QV4::ExecutionContext *context, QV4::Value *result, const QV4::Value &base, QV4::String *name); -void __qmljs_builtin_post_decrement_element(QV4::ExecutionContext *context, QV4::Value *result, const QV4::Value &base, const QV4::Value &index); - -void Q_NORETURN __qmljs_builtin_rethrow(QV4::ExecutionContext *context); -QV4::ExecutionContext *__qmljs_builtin_push_with_scope(const QV4::Value &o, QV4::ExecutionContext *ctx); -QV4::ExecutionContext *__qmljs_builtin_push_catch_scope(QV4::String *exceptionVarName, const QV4::Value &exceptionValue, QV4::ExecutionContext *ctx); -QV4::ExecutionContext *__qmljs_builtin_pop_scope(QV4::ExecutionContext *ctx); -void __qmljs_builtin_declare_var(QV4::ExecutionContext *ctx, bool deletable, QV4::String *name); -void __qmljs_builtin_define_property(QV4::ExecutionContext *ctx, const QV4::Value &object, QV4::String *name, QV4::Value *val); -void __qmljs_builtin_define_array(QV4::ExecutionContext *ctx, QV4::Value *array, QV4::Value *values, uint length); -void __qmljs_builtin_define_getter_setter(QV4::ExecutionContext *ctx, const QV4::Value &object, QV4::String *name, const QV4::Value *getter, const QV4::Value *setter); -void __qmljs_builtin_define_object_literal(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value *args, QV4::InternalClass *klass); - -// constructors -void __qmljs_init_closure(QV4::ExecutionContext *ctx, QV4::Value *result, QV4::Function *clos); -QV4::Function *__qmljs_register_function(QV4::ExecutionContext *ctx, QV4::String *name, - bool hasDirectEval, - bool usesArgumentsObject, bool isStrict, - bool hasNestedFunctions, - QV4::String **formals, unsigned formalCount, - QV4::String **locals, unsigned localCount); - -// strings -Q_QML_EXPORT double __qmljs_string_to_number(const QString &s); -QV4::Value __qmljs_string_from_number(QV4::ExecutionContext *ctx, double number); -QV4::String *__qmljs_string_concat(QV4::ExecutionContext *ctx, QV4::String *first, QV4::String *second); - -// objects -Q_QML_EXPORT QV4::Value __qmljs_object_default_value(QV4::Object *object, int typeHint); -void __qmljs_set_activation_property(QV4::ExecutionContext *ctx, QV4::String *name, const QV4::Value& value); -void __qmljs_set_property(QV4::ExecutionContext *ctx, const QV4::Value &object, QV4::String *name, const QV4::Value &value); -void __qmljs_get_property(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value &object, QV4::String *name); -void __qmljs_get_activation_property(QV4::ExecutionContext *ctx, QV4::Value *result, QV4::String *name); - -void __qmljs_call_global_lookup(QV4::ExecutionContext *context, QV4::Value *result, uint index, QV4::Value *args, int argc); -void __qmljs_construct_global_lookup(QV4::ExecutionContext *context, QV4::Value *result, uint index, QV4::Value *args, int argc); - - -void __qmljs_get_element(QV4::ExecutionContext *ctx, QV4::Value *retval, const QV4::Value &object, const QV4::Value &index); -void __qmljs_set_element(QV4::ExecutionContext *ctx, const QV4::Value &object, const QV4::Value &index, const QV4::Value &value); - -// For each -void __qmljs_foreach_iterator_object(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value &in); -void __qmljs_foreach_next_property_name(QV4::Value *result, const QV4::Value &foreach_iterator); - -// type conversion and testing -QV4::Value __qmljs_to_primitive(const QV4::Value &value, int typeHint); -QV4::Bool __qmljs_to_boolean(const QV4::Value &value); -double __qmljs_to_number(const QV4::Value &value); -QV4::Value __qmljs_to_string(const QV4::Value &value, QV4::ExecutionContext *ctx); -Q_QML_EXPORT QV4::String *__qmljs_convert_to_string(QV4::ExecutionContext *ctx, const QV4::Value &value); -void __qmljs_numberToString(QString *result, double num, int radix = 10); -QV4::Value __qmljs_to_object(QV4::ExecutionContext *ctx, const QV4::Value &value); -QV4::Object *__qmljs_convert_to_object(QV4::ExecutionContext *ctx, const QV4::Value &value); - -QV4::Bool __qmljs_equal_helper(const Value &x, const Value &y); -Q_QML_EXPORT QV4::Bool __qmljs_strict_equal(const QV4::Value &x, const QV4::Value &y); - -// unary operators -typedef void (*UnaryOpName)(QV4::Value *, const QV4::Value &); -void __qmljs_uplus(QV4::Value *result, const QV4::Value &value); -void __qmljs_uminus(QV4::Value *result, const QV4::Value &value); -void __qmljs_compl(QV4::Value *result, const QV4::Value &value); -void __qmljs_not(QV4::Value *result, const QV4::Value &value); -void __qmljs_increment(QV4::Value *result, const QV4::Value &value); -void __qmljs_decrement(QV4::Value *result, const QV4::Value &value); - -void __qmljs_delete_subscript(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value &base, const QV4::Value &index); -void __qmljs_delete_member(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value &base, QV4::String *name); -void __qmljs_delete_name(QV4::ExecutionContext *ctx, QV4::Value *result, QV4::String *name); - -void Q_NORETURN __qmljs_throw(QV4::ExecutionContext*, const QV4::Value &value); - -// binary operators -typedef void (*BinOp)(QV4::Value *result, const QV4::Value &left, const QV4::Value &right); -typedef void (*BinOpContext)(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value &left, const QV4::Value &right); - -void __qmljs_instanceof(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value &left, const QV4::Value &right); -void __qmljs_in(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value &left, const QV4::Value &right); -void __qmljs_add(ExecutionContext *ctx, QV4::Value *result, const QV4::Value &left, const QV4::Value &right); -void __qmljs_bit_or(QV4::Value *result, const QV4::Value &left, const QV4::Value &right); -void __qmljs_bit_xor(QV4::Value *result, const QV4::Value &left, const QV4::Value &right); -void __qmljs_bit_and(QV4::Value *result, const QV4::Value &left, const QV4::Value &right); -void __qmljs_sub(QV4::Value *result, const QV4::Value &left, const QV4::Value &right); -void __qmljs_mul(QV4::Value *result, const QV4::Value &left, const QV4::Value &right); -void __qmljs_div(QV4::Value *result, const QV4::Value &left, const QV4::Value &right); -void __qmljs_mod(QV4::Value *result, const QV4::Value &left, const QV4::Value &right); -void __qmljs_shl(QV4::Value *result, const QV4::Value &left, const QV4::Value &right); -void __qmljs_shr(QV4::Value *result, const QV4::Value &left, const QV4::Value &right); -void __qmljs_ushr(QV4::Value *result, const QV4::Value &left, const QV4::Value &right); -void __qmljs_gt(QV4::Value *result, const QV4::Value &left, const QV4::Value &right); -void __qmljs_lt(QV4::Value *result, const QV4::Value &left, const QV4::Value &right); -void __qmljs_ge(QV4::Value *result, const QV4::Value &left, const QV4::Value &right); -void __qmljs_le(QV4::Value *result, const QV4::Value &left, const QV4::Value &right); -void __qmljs_eq(QV4::Value *result, const QV4::Value &left, const QV4::Value &right); -void __qmljs_ne(QV4::Value *result, const QV4::Value &left, const QV4::Value &right); -void __qmljs_se(QV4::Value *result, const QV4::Value &left, const QV4::Value &right); -void __qmljs_sne(QV4::Value *result, const QV4::Value &left, const QV4::Value &right); - -void __qmljs_add_helper(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value &left, const QV4::Value &right); - - -typedef void (*InplaceBinOpName)(QV4::ExecutionContext *ctx, QV4::String *name, const QV4::Value &value); -void __qmljs_inplace_bit_and_name(QV4::ExecutionContext *ctx, QV4::String *name, const QV4::Value &value); -void __qmljs_inplace_bit_or_name(QV4::ExecutionContext *ctx, QV4::String *name, const QV4::Value &value); -void __qmljs_inplace_bit_xor_name(QV4::ExecutionContext *ctx, QV4::String *name, const QV4::Value &value); -void __qmljs_inplace_add_name(QV4::ExecutionContext *ctx, QV4::String *name, const QV4::Value &value); -void __qmljs_inplace_sub_name(QV4::ExecutionContext *ctx, QV4::String *name, const QV4::Value &value); -void __qmljs_inplace_mul_name(QV4::ExecutionContext *ctx, QV4::String *name, const QV4::Value &value); -void __qmljs_inplace_div_name(QV4::ExecutionContext *ctx, QV4::String *name, const QV4::Value &value); -void __qmljs_inplace_mod_name(QV4::ExecutionContext *ctx, QV4::String *name, const QV4::Value &value); -void __qmljs_inplace_shl_name(QV4::ExecutionContext *ctx, QV4::String *name, const QV4::Value &value); -void __qmljs_inplace_shr_name(QV4::ExecutionContext *ctx, QV4::String *name, const QV4::Value &value); -void __qmljs_inplace_ushr_name(QV4::ExecutionContext *ctx, QV4::String *name, const QV4::Value &value); - -typedef void (*InplaceBinOpElement)(QV4::ExecutionContext *ctx, const QV4::Value &base, const QV4::Value &index, const QV4::Value &rhs); -void __qmljs_inplace_bit_and_element(QV4::ExecutionContext *ctx, const QV4::Value &base, const QV4::Value &index, const QV4::Value &rhs); -void __qmljs_inplace_bit_or_element(QV4::ExecutionContext *ctx, const QV4::Value &base, const QV4::Value &index, const QV4::Value &rhs); -void __qmljs_inplace_bit_xor_element(QV4::ExecutionContext *ctx, const QV4::Value &base, const QV4::Value &index, const QV4::Value &rhs); -void __qmljs_inplace_add_element(QV4::ExecutionContext *ctx, const QV4::Value &base, const QV4::Value &index, const QV4::Value &rhs); -void __qmljs_inplace_sub_element(QV4::ExecutionContext *ctx, const QV4::Value &base, const QV4::Value &index, const QV4::Value &rhs); -void __qmljs_inplace_mul_element(QV4::ExecutionContext *ctx, const QV4::Value &base, const QV4::Value &index, const QV4::Value &rhs); -void __qmljs_inplace_div_element(QV4::ExecutionContext *ctx, const QV4::Value &base, const QV4::Value &index, const QV4::Value &rhs); -void __qmljs_inplace_mod_element(QV4::ExecutionContext *ctx, const QV4::Value &base, const QV4::Value &index, const QV4::Value &rhs); -void __qmljs_inplace_shl_element(QV4::ExecutionContext *ctx, const QV4::Value &base, const QV4::Value &index, const QV4::Value &rhs); -void __qmljs_inplace_shr_element(QV4::ExecutionContext *ctx, const QV4::Value &base, const QV4::Value &index, const QV4::Value &rhs); -void __qmljs_inplace_ushr_element(QV4::ExecutionContext *ctx, const QV4::Value &base, const QV4::Value &index, const QV4::Value &rhs); - -typedef void (*InplaceBinOpMember)(QV4::ExecutionContext *ctx, const QV4::Value &base, QV4::String *name, const QV4::Value &rhs); -void __qmljs_inplace_bit_and_member(QV4::ExecutionContext *ctx, const QV4::Value &base, QV4::String *name, const QV4::Value &rhs); -void __qmljs_inplace_bit_or_member(QV4::ExecutionContext *ctx, const QV4::Value &base, QV4::String *name, const QV4::Value &rhs); -void __qmljs_inplace_bit_xor_member(QV4::ExecutionContext *ctx, const QV4::Value &base, QV4::String *name, const QV4::Value &rhs); -void __qmljs_inplace_add_member(QV4::ExecutionContext *ctx, const QV4::Value &base, QV4::String *name, const QV4::Value &rhs); -void __qmljs_inplace_sub_member(QV4::ExecutionContext *ctx, const QV4::Value &base, QV4::String *name, const QV4::Value &rhs); -void __qmljs_inplace_mul_member(QV4::ExecutionContext *ctx, const QV4::Value &base, QV4::String *name, const QV4::Value &rhs); -void __qmljs_inplace_div_member(QV4::ExecutionContext *ctx, const QV4::Value &base, QV4::String *name, const QV4::Value &rhs); -void __qmljs_inplace_mod_member(QV4::ExecutionContext *ctx, const QV4::Value &base, QV4::String *name, const QV4::Value &rhs); -void __qmljs_inplace_shl_member(QV4::ExecutionContext *ctx, const QV4::Value &base, QV4::String *name, const QV4::Value &rhs); -void __qmljs_inplace_shr_member(QV4::ExecutionContext *ctx, const QV4::Value &base, QV4::String *name, const QV4::Value &rhs); -void __qmljs_inplace_ushr_member(QV4::ExecutionContext *ctx, const QV4::Value &base, QV4::String *name, const QV4::Value &rhs); - -typedef QV4::Bool (*CmpOp)(const QV4::Value &left, const QV4::Value &right); -QV4::Bool __qmljs_cmp_gt(const QV4::Value &left, const QV4::Value &right); -QV4::Bool __qmljs_cmp_lt(const QV4::Value &left, const QV4::Value &right); -QV4::Bool __qmljs_cmp_ge(const QV4::Value &left, const QV4::Value &right); -QV4::Bool __qmljs_cmp_le(const QV4::Value &left, const QV4::Value &right); -QV4::Bool __qmljs_cmp_eq(const QV4::Value &left, const QV4::Value &right); -QV4::Bool __qmljs_cmp_ne(const QV4::Value &left, const QV4::Value &right); -QV4::Bool __qmljs_cmp_se(const QV4::Value &left, const QV4::Value &right); -QV4::Bool __qmljs_cmp_sne(const QV4::Value &left, const QV4::Value &right); - -typedef QV4::Bool (*CmpOpContext)(QV4::ExecutionContext *ctx, const QV4::Value &left, const QV4::Value &right); -QV4::Bool __qmljs_cmp_instanceof(QV4::ExecutionContext *ctx, const QV4::Value &left, const QV4::Value &right); -QV4::Bool __qmljs_cmp_in(QV4::ExecutionContext *ctx, const QV4::Value &left, const QV4::Value &right); - -// type conversion and testing -inline QV4::Value __qmljs_to_primitive(const QV4::Value &value, int typeHint) -{ - QV4::Object *o = value.asObject(); - if (!o) - return value; - return __qmljs_object_default_value(o, typeHint); -} - -inline double __qmljs_to_number(const QV4::Value &value) -{ - switch (value.type()) { - case QV4::Value::Undefined_Type: - return std::numeric_limits::quiet_NaN(); - case QV4::Value::Null_Type: - return 0; - case QV4::Value::Boolean_Type: - return (value.booleanValue() ? 1. : 0.); - case QV4::Value::Integer_Type: - return value.int_32; - case QV4::Value::String_Type: - return __qmljs_string_to_number(value.stringValue()->toQString()); - case QV4::Value::Object_Type: { - QV4::Value prim = __qmljs_to_primitive(value, QV4::NUMBER_HINT); - return __qmljs_to_number(prim); - } - default: // double - return value.doubleValue(); - } -} - -inline QV4::Value __qmljs_to_string(const QV4::Value &value, QV4::ExecutionContext *ctx) -{ - if (value.isString()) - return value; - return QV4::Value::fromString(__qmljs_convert_to_string(ctx, value)); -} - -inline QV4::Value __qmljs_to_object(QV4::ExecutionContext *ctx, const QV4::Value &value) -{ - if (value.isObject()) - return value; - return QV4::Value::fromObject(__qmljs_convert_to_object(ctx, value)); -} - - -inline void __qmljs_uplus(QV4::Value *result, const QV4::Value &value) -{ - TRACE1(value); - - *result = value; - if (result->tryIntegerConversion()) - return; - - double n = __qmljs_to_number(value); - *result = QV4::Value::fromDouble(n); -} - -inline void __qmljs_uminus(QV4::Value *result, const QV4::Value &value) -{ - TRACE1(value); - - // +0 != -0, so we need to convert to double when negating 0 - if (value.isInteger() && value.integerValue()) - *result = QV4::Value::fromInt32(-value.integerValue()); - else { - double n = __qmljs_to_number(value); - *result = QV4::Value::fromDouble(-n); - } -} - -inline void __qmljs_compl(QV4::Value *result, const QV4::Value &value) -{ - TRACE1(value); - - int n; - if (value.isConvertibleToInt()) - n = value.int_32; - else - n = QV4::Value::toInt32(__qmljs_to_number(value)); - - *result = QV4::Value::fromInt32(~n); -} - -inline void __qmljs_not(QV4::Value *result, const QV4::Value &value) -{ - TRACE1(value); - - bool b = value.toBoolean(); - *result = QV4::Value::fromBoolean(!b); -} - -// binary operators -inline void __qmljs_bit_or(QV4::Value *result, const QV4::Value &left, const QV4::Value &right) -{ - TRACE2(left, right); - - if (QV4::Value::integerCompatible(left, right)) { - *result = QV4::Value::fromInt32(left.integerValue() | right.integerValue()); - return; - } - - int lval = QV4::Value::toInt32(__qmljs_to_number(left)); - int rval = QV4::Value::toInt32(__qmljs_to_number(right)); - *result = QV4::Value::fromInt32(lval | rval); -} - -inline void __qmljs_bit_xor(QV4::Value *result, const QV4::Value &left, const QV4::Value &right) -{ - TRACE2(left, right); - - if (QV4::Value::integerCompatible(left, right)) { - *result = QV4::Value::fromInt32(left.integerValue() ^ right.integerValue()); - return; - } - - int lval = QV4::Value::toInt32(__qmljs_to_number(left)); - int rval = QV4::Value::toInt32(__qmljs_to_number(right)); - *result = QV4::Value::fromInt32(lval ^ rval); -} - -inline void __qmljs_bit_and(QV4::Value *result, const QV4::Value &left, const QV4::Value &right) -{ - TRACE2(left, right); - - if (QV4::Value::integerCompatible(left, right)) { - *result = QV4::Value::fromInt32(left.integerValue() & right.integerValue()); - return; - } - - int lval = QV4::Value::toInt32(__qmljs_to_number(left)); - int rval = QV4::Value::toInt32(__qmljs_to_number(right)); - *result = QV4::Value::fromInt32(lval & rval); -} - -inline void __qmljs_add(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value &left, const QV4::Value &right) -{ - TRACE2(left, right); - - if (QV4::Value::integerCompatible(left, right)) { - *result = add_int32(left.integerValue(), right.integerValue()); - return; - } - - if (QV4::Value::bothDouble(left, right)) { - *result = QV4::Value::fromDouble(left.doubleValue() + right.doubleValue()); - return; - } - - __qmljs_add_helper(ctx, result, left, right); -} - -inline void __qmljs_sub(QV4::Value *result, const QV4::Value &left, const QV4::Value &right) -{ - TRACE2(left, right); - - if (QV4::Value::integerCompatible(left, right)) { - *result = sub_int32(left.integerValue(), right.integerValue()); - return; - } - - double lval = __qmljs_to_number(left); - double rval = __qmljs_to_number(right); - *result = QV4::Value::fromDouble(lval - rval); -} - -inline void __qmljs_mul(QV4::Value *result, const QV4::Value &left, const QV4::Value &right) -{ - TRACE2(left, right); - - if (QV4::Value::integerCompatible(left, right)) { - *result = mul_int32(left.integerValue(), right.integerValue()); - return; - } - - double lval = __qmljs_to_number(left); - double rval = __qmljs_to_number(right); - *result = QV4::Value::fromDouble(lval * rval); -} - -inline void __qmljs_div(QV4::Value *result, const QV4::Value &left, const QV4::Value &right) -{ - TRACE2(left, right); - - double lval = __qmljs_to_number(left); - double rval = __qmljs_to_number(right); - *result = QV4::Value::fromDouble(lval / rval); -} - -inline void __qmljs_mod(QV4::Value *result, const QV4::Value &left, const QV4::Value &right) -{ - TRACE2(left, right); - - if (QV4::Value::integerCompatible(left, right) && right.integerValue() != 0) { - int intRes = left.integerValue() % right.integerValue(); - if (intRes != 0 || left.integerValue() >= 0) { - *result = QV4::Value::fromInt32(intRes); - return; - } - } - - double lval = __qmljs_to_number(left); - double rval = __qmljs_to_number(right); - *result = QV4::Value::fromDouble(std::fmod(lval, rval)); -} - -inline void __qmljs_shl(QV4::Value *result, const QV4::Value &left, const QV4::Value &right) -{ - TRACE2(left, right); - - if (QV4::Value::integerCompatible(left, right)) { - *result = QV4::Value::fromInt32(left.integerValue() << ((uint(right.integerValue()) & 0x1f))); - return; - } - - int lval = QV4::Value::toInt32(__qmljs_to_number(left)); - unsigned rval = QV4::Value::toUInt32(__qmljs_to_number(right)) & 0x1f; - *result = QV4::Value::fromInt32(lval << rval); -} - -inline void __qmljs_shr(QV4::Value *result, const QV4::Value &left, const QV4::Value &right) -{ - TRACE2(left, right); - - if (QV4::Value::integerCompatible(left, right)) { - *result = QV4::Value::fromInt32(left.integerValue() >> ((uint(right.integerValue()) & 0x1f))); - return; - } - - int lval = QV4::Value::toInt32(__qmljs_to_number(left)); - unsigned rval = QV4::Value::toUInt32(__qmljs_to_number(right)) & 0x1f; - *result = QV4::Value::fromInt32(lval >> rval); -} - -inline void __qmljs_ushr(QV4::Value *result, const QV4::Value &left, const QV4::Value &right) -{ - TRACE2(left, right); - - uint res; - if (QV4::Value::integerCompatible(left, right)) { - res = uint(left.integerValue()) >> (uint(right.integerValue()) & 0x1f); - } else { - unsigned lval = QV4::Value::toUInt32(__qmljs_to_number(left)); - unsigned rval = QV4::Value::toUInt32(__qmljs_to_number(right)) & 0x1f; - res = lval >> rval; - } - - if (res > INT_MAX) - *result = QV4::Value::fromDouble(res); - else - *result = QV4::Value::fromInt32(res); -} - -inline void __qmljs_gt(QV4::Value *result, const QV4::Value &left, const QV4::Value &right) -{ - TRACE2(left, right); - - *result = QV4::Value::fromBoolean(__qmljs_cmp_gt(left, right)); -} - -inline void __qmljs_lt(QV4::Value *result, const QV4::Value &left, const QV4::Value &right) -{ - TRACE2(left, right); - - *result = QV4::Value::fromBoolean(__qmljs_cmp_lt(left, right)); -} - -inline void __qmljs_ge(QV4::Value *result, const QV4::Value &left, const QV4::Value &right) -{ - TRACE2(left, right); - - *result = QV4::Value::fromBoolean(__qmljs_cmp_ge(left, right)); -} - -inline void __qmljs_le(QV4::Value *result, const QV4::Value &left, const QV4::Value &right) -{ - TRACE2(left, right); - - *result = QV4::Value::fromBoolean(__qmljs_cmp_le(left, right)); -} - -inline void __qmljs_eq(QV4::Value *result, const QV4::Value &left, const QV4::Value &right) -{ - TRACE2(left, right); - - *result = QV4::Value::fromBoolean(__qmljs_cmp_eq(left, right)); -} - -inline void __qmljs_ne(QV4::Value *result, const QV4::Value &left, const QV4::Value &right) -{ - TRACE2(left, right); - - *result = QV4::Value::fromBoolean(!__qmljs_cmp_eq(left, right)); -} - -inline void __qmljs_se(QV4::Value *result, const QV4::Value &left, const QV4::Value &right) -{ - TRACE2(left, right); - - bool r = __qmljs_strict_equal(left, right); - *result = QV4::Value::fromBoolean(r); -} - -inline void __qmljs_sne(QV4::Value *result, const QV4::Value &left, const QV4::Value &right) -{ - TRACE2(left, right); - - bool r = ! __qmljs_strict_equal(left, right); - *result = QV4::Value::fromBoolean(r); -} - -inline QV4::Bool __qmljs_cmp_gt(const QV4::Value &left, const QV4::Value &right) -{ - TRACE2(left, right); - if (QV4::Value::integerCompatible(left, right)) - return left.integerValue() > right.integerValue(); - - QV4::Value l = __qmljs_to_primitive(left, QV4::NUMBER_HINT); - QV4::Value r = __qmljs_to_primitive(right, QV4::NUMBER_HINT); - - if (QV4::Value::bothDouble(l, r)) { - return l.doubleValue() > r.doubleValue(); - } else if (l.isString() && r.isString()) { - return r.stringValue()->compare(l.stringValue()); - } else { - double dl = __qmljs_to_number(l); - double dr = __qmljs_to_number(r); - return dl > dr; - } -} - -inline QV4::Bool __qmljs_cmp_lt(const QV4::Value &left, const QV4::Value &right) -{ - TRACE2(left, right); - if (QV4::Value::integerCompatible(left, right)) - return left.integerValue() < right.integerValue(); - - QV4::Value l = __qmljs_to_primitive(left, QV4::NUMBER_HINT); - QV4::Value r = __qmljs_to_primitive(right, QV4::NUMBER_HINT); - - if (QV4::Value::bothDouble(l, r)) { - return l.doubleValue() < r.doubleValue(); - } else if (l.isString() && r.isString()) { - return l.stringValue()->compare(r.stringValue()); - } else { - double dl = __qmljs_to_number(l); - double dr = __qmljs_to_number(r); - return dl < dr; - } -} - -inline QV4::Bool __qmljs_cmp_ge(const QV4::Value &left, const QV4::Value &right) -{ - TRACE2(left, right); - if (QV4::Value::integerCompatible(left, right)) - return left.integerValue() >= right.integerValue(); - - QV4::Value l = __qmljs_to_primitive(left, QV4::NUMBER_HINT); - QV4::Value r = __qmljs_to_primitive(right, QV4::NUMBER_HINT); - - if (QV4::Value::bothDouble(l, r)) { - return l.doubleValue() >= r.doubleValue(); - } else if (l.isString() && r.isString()) { - return !l.stringValue()->compare(r.stringValue()); - } else { - double dl = __qmljs_to_number(l); - double dr = __qmljs_to_number(r); - return dl >= dr; - } -} - -inline QV4::Bool __qmljs_cmp_le(const QV4::Value &left, const QV4::Value &right) -{ - TRACE2(left, right); - if (QV4::Value::integerCompatible(left, right)) - return left.integerValue() <= right.integerValue(); - - QV4::Value l = __qmljs_to_primitive(left, QV4::NUMBER_HINT); - QV4::Value r = __qmljs_to_primitive(right, QV4::NUMBER_HINT); - - if (QV4::Value::bothDouble(l, r)) { - return l.doubleValue() <= r.doubleValue(); - } else if (l.isString() && r.isString()) { - return !r.stringValue()->compare(l.stringValue()); - } else { - double dl = __qmljs_to_number(l); - double dr = __qmljs_to_number(r); - return dl <= dr; - } -} - -inline QV4::Bool __qmljs_cmp_eq(const QV4::Value &left, const QV4::Value &right) -{ - TRACE2(left, right); - - if (left.val == right.val) - // NaN != NaN - return (left.tag & QV4::Value::NotDouble_Mask) != QV4::Value::NaN_Mask; - - if (left.type() == right.type()) { - if (left.isManaged()) - return left.managed()->isEqualTo(right.managed()); - return false; - } - - return __qmljs_equal_helper(left, right); -} - -inline QV4::Bool __qmljs_cmp_ne(const QV4::Value &left, const QV4::Value &right) -{ - TRACE2(left, right); - - return !__qmljs_cmp_eq(left, right); -} - -inline QV4::Bool __qmljs_cmp_se(const QV4::Value &left, const QV4::Value &right) -{ - TRACE2(left, right); - - return __qmljs_strict_equal(left, right); -} - -inline QV4::Bool __qmljs_cmp_sne(const QV4::Value &left, const QV4::Value &right) -{ - TRACE2(left, right); - - return ! __qmljs_strict_equal(left, right); -} - -inline QV4::Bool __qmljs_cmp_instanceof(QV4::ExecutionContext *ctx, const QV4::Value &left, const QV4::Value &right) -{ - TRACE2(left, right); - - QV4::Value v; - __qmljs_instanceof(ctx, &v, left, right); - return v.booleanValue(); -} - -inline uint __qmljs_cmp_in(QV4::ExecutionContext *ctx, const QV4::Value &left, const QV4::Value &right) -{ - TRACE2(left, right); - - QV4::Value v; - __qmljs_in(ctx, &v, left, right); - return v.booleanValue(); -} - -} // namespace QV4 - -QT_END_NAMESPACE - -#endif // QMLJS_RUNTIME_H diff --git a/src/qml/qml/v4/qv4script.cpp b/src/qml/qml/v4/qv4script.cpp deleted file mode 100644 index 3de218a451..0000000000 --- a/src/qml/qml/v4/qv4script.cpp +++ /dev/null @@ -1,249 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4script_p.h" -#include "qv4mm_p.h" -#include "qv4functionobject_p.h" -#include "qv4function_p.h" -#include "qv4context_p.h" -#include "qv4debugging_p.h" -#include "qv4exception_p.h" - -#include -#include -#include -#include -#include -#include - -#include -#include - -using namespace QV4; - -struct QmlBindingWrapper : FunctionObject -{ - QmlBindingWrapper(ExecutionContext *scope, Function *f, Object *qml) - : FunctionObject(scope, scope->engine->id_eval) - , qml(qml) - { - vtbl = &static_vtbl; - function = f; - usesArgumentsObject = function->usesArgumentsObject; - needsActivation = function->needsActivation(); - defineReadonlyProperty(scope->engine->id_length, Value::fromInt32(1)); - - qmlContext = scope->engine->newQmlContext(this, qml); - scope->engine->popContext(); - } - - static Value call(Managed *that, const Value &, Value *, int); - static void markObjects(Managed *m) - { - QmlBindingWrapper *wrapper = static_cast(m); - if (wrapper->qml) - wrapper->qml->mark(); - FunctionObject::markObjects(m); - wrapper->qmlContext->mark(); - } - -protected: - static const ManagedVTable static_vtbl; - -private: - Object *qml; - CallContext *qmlContext; - -}; - -DEFINE_MANAGED_VTABLE(QmlBindingWrapper); - -Value QmlBindingWrapper::call(Managed *that, const Value &, Value *, int) -{ - ExecutionEngine *engine = that->engine(); - QmlBindingWrapper *This = static_cast(that); - - CallContext *ctx = This->qmlContext; - std::fill(ctx->locals, ctx->locals + ctx->function->varCount, Value::undefinedValue()); - engine->pushContext(ctx); - Value result = This->function->code(ctx, This->function->codeData); - engine->popContext(); - - return result; - -} - - -void Script::parse() -{ - if (parsed) - return; - - using namespace QQmlJS; - - parsed = true; - - ExecutionEngine *v4 = scope->engine; - - MemoryManager::GCBlocker gcBlocker(v4->memoryManager); - - V4IR::Module module; - - QQmlJS::Engine ee, *engine = ⅇ - Lexer lexer(engine); - lexer.setCode(sourceCode, line, parseAsBinding); - Parser parser(engine); - - const bool parsed = parser.parseProgram(); - - DiagnosticMessage *error = 0, **errIt = &error; - foreach (const QQmlJS::DiagnosticMessage &m, parser.diagnosticMessages()) { - if (m.isError()) { - *errIt = new DiagnosticMessage; - (*errIt)->fileName = sourceFile; - (*errIt)->offset = m.loc.offset; - (*errIt)->length = m.loc.length; - (*errIt)->startLine = m.loc.startLine; - (*errIt)->startColumn = m.loc.startColumn; - (*errIt)->type = DiagnosticMessage::Error; - (*errIt)->message = m.message; - errIt = &(*errIt)->next; - } else { - qWarning() << sourceFile << ':' << m.loc.startLine << ':' << m.loc.startColumn - << ": warning: " << m.message; - } - } - if (error) - scope->throwSyntaxError(error); - - if (parsed) { - using namespace AST; - Program *program = AST::cast(parser.rootNode()); - if (!program) { - // if parsing was successful, and we have no program, then - // we're done...: - return; - } - - QStringList inheritedLocals; - if (inheritContext) - for (String * const *i = scope->variables(), * const *ei = i + scope->variableCount(); i < ei; ++i) - inheritedLocals.append(*i ? (*i)->toQString() : QString()); - - Codegen cg(scope, strictMode); - V4IR::Function *globalIRCode = cg(sourceFile, sourceCode, program, &module, - parseAsBinding ? QQmlJS::Codegen::QmlBinding : QQmlJS::Codegen::EvalCode, inheritedLocals); - QScopedPointer isel(v4->iselFactory->create(v4, &module)); - if (inheritContext) - isel->setUseFastLookups(false); - if (globalIRCode) - vmFunction = isel->vmFunction(globalIRCode); - } - - if (!vmFunction) - // ### FIX file/line number - v4->current->throwError(QV4::Value::fromObject(v4->newSyntaxErrorObject(v4->current, 0))); -} - -Value Script::run() -{ - if (!parsed) - parse(); - if (!vmFunction) - return Value::undefinedValue(); - - QV4::ExecutionEngine *engine = scope->engine; - - if (qml.isEmpty()) { - TemporaryAssignment savedGlobalCode(engine->globalCode, vmFunction); - - bool strict = scope->strictMode; - Lookup *lookups = scope->lookups; - - scope->strictMode = vmFunction->isStrict; - scope->lookups = vmFunction->lookups; - - QV4::Value result; - try { - result = vmFunction->code(scope, vmFunction->codeData); - } catch (Exception &e) { - scope->strictMode = strict; - scope->lookups = lookups; - throw; - } - - return result; - - } else { - FunctionObject *f = new (engine->memoryManager) QmlBindingWrapper(scope, vmFunction, qml.value().asObject()); - return f->call(Value::undefinedValue(), 0, 0); - } -} - -Function *Script::function() -{ - if (!parsed) - parse(); - return vmFunction; -} - -Value Script::qmlBinding() -{ - if (!parsed) - parse(); - QV4::ExecutionEngine *v4 = scope->engine; - return Value::fromObject(new (v4->memoryManager) QmlBindingWrapper(scope, vmFunction, qml.value().asObject())); -} - -QV4::Value Script::evaluate(ExecutionEngine *engine, const QString &script, Object *scopeObject) -{ - QV4::Script qmlScript(engine, scopeObject, script, QString()); - - QV4::ExecutionContext *ctx = engine->current; - QV4::Value result = QV4::Value::undefinedValue(); - try { - qmlScript.parse(); - result = qmlScript.run(); - } catch (QV4::Exception &e) { - e.accept(ctx); - } - return result; -} diff --git a/src/qml/qml/v4/qv4script_p.h b/src/qml/qml/v4/qv4script_p.h deleted file mode 100644 index 274a87db26..0000000000 --- a/src/qml/qml/v4/qv4script_p.h +++ /dev/null @@ -1,88 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QV4SCRIPT_H -#define QV4SCRIPT_H - -#include "qv4global_p.h" -#include "qv4engine_p.h" - -QT_BEGIN_NAMESPACE - -namespace QV4 { - -struct ExecutionContext; - -struct Q_QML_EXPORT Script { - Script(ExecutionContext *scope, const QString &sourceCode, const QString &source = QString(), int line = 1, int column = 0) - : sourceFile(source), line(line), column(column), sourceCode(sourceCode) - , scope(scope), strictMode(false), inheritContext(false), parsed(false) - , vmFunction(0), parseAsBinding(false) {} - Script(ExecutionEngine *engine, Object *qml, const QString &sourceCode, const QString &source = QString(), int line = 1, int column = 0) - : sourceFile(source), line(line), column(column), sourceCode(sourceCode) - , scope(engine->rootContext), strictMode(false), inheritContext(true), parsed(false) - , qml(Value::fromObject(qml)), vmFunction(0), parseAsBinding(true) {} - QString sourceFile; - int line; - int column; - QString sourceCode; - ExecutionContext *scope; - bool strictMode; - bool inheritContext; - bool parsed; - QV4::PersistentValue qml; - Function *vmFunction; - bool parseAsBinding; - - void parse(); - Value run(); - Value qmlBinding(); - - Function *function(); - - - static Value evaluate(ExecutionEngine *engine, const QString &script, Object *scopeObject); -}; - -} - -QT_END_NAMESPACE - -#endif diff --git a/src/qml/qml/v4/qv4sequenceobject.cpp b/src/qml/qml/v4/qv4sequenceobject.cpp deleted file mode 100644 index c4d9a71519..0000000000 --- a/src/qml/qml/v4/qv4sequenceobject.cpp +++ /dev/null @@ -1,651 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include - -#include "qv4sequenceobject_p.h" - -#include -#include -#include - -QT_BEGIN_NAMESPACE - -using namespace QV4; - -// helper function to generate valid warnings if errors occur during sequence operations. -static void generateWarning(QV4::ExecutionContext *ctx, const QString& description) -{ - QQmlEngine *engine = ctx->engine->v8Engine->engine(); - if (!engine) - return; - QQmlError retn; - retn.setDescription(description); - - QV4::ExecutionEngine::StackFrame frame = ctx->engine->currentStackFrame(); - - retn.setLine(frame.line); - retn.setUrl(QUrl(frame.source)); - QQmlEnginePrivate::warning(engine, retn); -} - -// F(elementType, elementTypeName, sequenceType, defaultValue) -#define FOREACH_QML_SEQUENCE_TYPE(F) \ - F(int, Int, QList, 0) \ - F(qreal, Real, QList, 0.0) \ - F(bool, Bool, QList, false) \ - F(QString, String, QList, QString()) \ - F(QString, QString, QStringList, QString()) \ - F(QUrl, Url, QList, QUrl()) - -static QV4::Value convertElementToValue(QV4::ExecutionEngine *engine, const QString &element) -{ - return QV4::Value::fromString(engine, element); -} - -static QV4::Value convertElementToValue(QV4::ExecutionEngine *, int element) -{ - return QV4::Value::fromInt32(element); -} - -static QV4::Value convertElementToValue(QV4::ExecutionEngine *engine, const QUrl &element) -{ - return QV4::Value::fromString(engine, element.toString()); -} - -static QV4::Value convertElementToValue(QV4::ExecutionEngine *, qreal element) -{ - return QV4::Value::fromDouble(element); -} - -static QV4::Value convertElementToValue(QV4::ExecutionEngine *, bool element) -{ - return QV4::Value::fromBoolean(element); -} - -static QString convertElementToString(const QString &element) -{ - return element; -} - -static QString convertElementToString(int element) -{ - return QString::number(element); -} - -static QString convertElementToString(const QUrl &element) -{ - return element.toString(); -} - -static QString convertElementToString(qreal element) -{ - QString qstr; - __qmljs_numberToString(&qstr, element, 10); - return qstr; -} - -static QString convertElementToString(bool element) -{ - if (element) - return QStringLiteral("true"); - else - return QStringLiteral("false"); -} - -template ElementType convertValueToElement(const QV4::Value &value); - -template <> QString convertValueToElement(const QV4::Value &value) -{ - return value.toQString(); -} - -template <> int convertValueToElement(const QV4::Value &value) -{ - return value.toInt32(); -} - -template <> QUrl convertValueToElement(const QV4::Value &value) -{ - return QUrl(value.toQString()); -} - -template <> qreal convertValueToElement(const QV4::Value &value) -{ - return value.toNumber(); -} - -template <> bool convertValueToElement(const QV4::Value &value) -{ - return value.toBoolean(); -} - -template -class QQmlSequence : public QV4::Object -{ - Q_MANAGED -public: - QQmlSequence(QV4::ExecutionEngine *engine, const Container &container) - : QV4::Object(engine) - , m_container(container) - , m_object(0) - , m_propertyIndex(-1) - , m_isReference(false) - { - type = Type_QmlSequence; - vtbl = &static_vtbl; - prototype = engine->sequencePrototype; - init(engine); - } - - QQmlSequence(QV4::ExecutionEngine *engine, QObject *object, int propertyIndex) - : QV4::Object(engine) - , m_object(object) - , m_propertyIndex(propertyIndex) - , m_isReference(true) - { - type = Type_QmlSequence; - vtbl = &static_vtbl; - prototype = engine->sequencePrototype; - loadReference(); - init(engine); - } - - void init(ExecutionEngine *engine) - { - defineAccessorProperty(engine, QStringLiteral("length"), method_get_length, method_set_length); - } - - QV4::Value containerGetIndexed(uint index, bool *hasProperty) - { - /* Qt containers have int (rather than uint) allowable indexes. */ - if (index > INT_MAX) { - generateWarning(engine()->current, QLatin1String("Index out of range during indexed get")); - if (hasProperty) - *hasProperty = false; - return QV4::Value::undefinedValue(); - } - if (m_isReference) { - if (!m_object) { - if (hasProperty) - *hasProperty = false; - return QV4::Value::undefinedValue(); - } - loadReference(); - } - qint32 signedIdx = static_cast(index); - if (signedIdx < m_container.count()) { - if (hasProperty) - *hasProperty = true; - return convertElementToValue(engine(), m_container.at(signedIdx)); - } - if (hasProperty) - *hasProperty = false; - return QV4::Value::undefinedValue(); - } - - void containerPutIndexed(uint index, const QV4::Value &value) - { - /* Qt containers have int (rather than uint) allowable indexes. */ - if (index > INT_MAX) { - generateWarning(engine()->current, QLatin1String("Index out of range during indexed set")); - return; - } - - if (m_isReference) { - if (!m_object) - return; - loadReference(); - } - - qint32 signedIdx = static_cast(index); - - int count = m_container.count(); - - typename Container::value_type element = convertValueToElement(value); - - if (signedIdx == count) { - m_container.append(element); - } else if (signedIdx < count) { - m_container[signedIdx] = element; - } else { - /* according to ECMA262r3 we need to insert */ - /* the value at the given index, increasing length to index+1. */ - m_container.reserve(signedIdx + 1); - while (signedIdx > count++) { - m_container.append(typename Container::value_type()); - } - m_container.append(element); - } - - if (m_isReference) - storeReference(); - } - - QV4::PropertyAttributes containerQueryIndexed(uint index) const - { - /* Qt containers have int (rather than uint) allowable indexes. */ - if (index > INT_MAX) { - generateWarning(engine()->current, QLatin1String("Index out of range during indexed query")); - return QV4::Attr_Invalid; - } - if (m_isReference) { - if (!m_object) - return QV4::Attr_Invalid; - loadReference(); - } - qint32 signedIdx = static_cast(index); - return (signedIdx < m_container.count()) ? QV4::Attr_Data : QV4::Attr_Invalid; - } - - Property *containerAdvanceIterator(ObjectIterator *it, String **name, uint *index, PropertyAttributes *attrs) - { - *name = 0; - *index = UINT_MAX; - - if (m_isReference) { - if (!m_object) - return QV4::Object::advanceIterator(this, it, name, index, attrs); - loadReference(); - } - - if (it->arrayIndex < m_container.count()) { - if (attrs) - *attrs = QV4::Attr_Data; - *index = it->arrayIndex; - ++it->arrayIndex; - it->tmpDynamicProperty.value = convertElementToValue(engine(), m_container.at(*index)); - return &it->tmpDynamicProperty; - } - return QV4::Object::advanceIterator(this, it, name, index, attrs); - } - - bool containerDeleteIndexedProperty(uint index) - { - /* Qt containers have int (rather than uint) allowable indexes. */ - if (index > INT_MAX) - return false; - if (m_isReference) { - if (!m_object) - return false; - loadReference(); - } - qint32 signedIdx = static_cast(index); - - if (signedIdx >= m_container.count()) - return false; - - /* according to ECMA262r3 it should be Undefined, */ - /* but we cannot, so we insert a default-value instead. */ - m_container.replace(signedIdx, typename Container::value_type()); - - if (m_isReference) - storeReference(); - - return true; - } - - bool containerIsEqualTo(Managed *other) - { - QQmlSequence *otherSequence = other->as >(); - if (!otherSequence) - return false; - if (m_isReference && otherSequence->m_isReference) { - return m_object == otherSequence->m_object && m_propertyIndex == otherSequence->m_propertyIndex; - } else if (!m_isReference && !otherSequence->m_isReference) { - return this == otherSequence; - } - return false; - } - - struct DefaultCompareFunctor - { - bool operator()(typename Container::value_type lhs, typename Container::value_type rhs) - { - return convertElementToString(lhs) < convertElementToString(rhs); - } - }; - - struct CompareFunctor - { - CompareFunctor(QV4::ExecutionContext *ctx, const QV4::Value &compareFn) - : m_ctx(ctx), m_compareFn(compareFn) - {} - - bool operator()(typename Container::value_type lhs, typename Container::value_type rhs) - { - QV4::Managed *fun = this->m_compareFn.asManaged(); - QV4::Value argv[2] = { - convertElementToValue(this->m_ctx->engine, lhs), - convertElementToValue(this->m_ctx->engine, rhs) - }; - QV4::Value result = fun->call(QV4::Value::fromObject(this->m_ctx->engine->globalObject), argv, 2); - return result.toNumber() < 0; - } - - private: - QV4::ExecutionContext *m_ctx; - QV4::Value m_compareFn; - }; - - void sort(QV4::SimpleCallContext *ctx) - { - if (m_isReference) { - if (!m_object) - return; - loadReference(); - } - - if (ctx->argumentCount == 1 && ctx->arguments[0].asFunctionObject()) { - QV4::Value compareFn = ctx->arguments[0]; - CompareFunctor cf(ctx, compareFn); - qSort(m_container.begin(), m_container.end(), cf); - } else { - DefaultCompareFunctor cf; - qSort(m_container.begin(), m_container.end(), cf); - } - - if (m_isReference) - storeReference(); - } - - static QV4::Value method_get_length(QV4::SimpleCallContext *ctx) - { - QQmlSequence *This = ctx->thisObject.as >(); - if (!This) - ctx->throwTypeError(); - - if (This->m_isReference) { - if (!This->m_object) - return QV4::Value::fromInt32(0); - This->loadReference(); - } - return QV4::Value::fromInt32(This->m_container.count()); - } - - static QV4::Value method_set_length(QV4::SimpleCallContext* ctx) - { - QQmlSequence *This = ctx->thisObject.as >(); - if (!This) - ctx->throwTypeError(); - - quint32 newLength = ctx->arguments[0].toUInt32(); - /* Qt containers have int (rather than uint) allowable indexes. */ - if (newLength > INT_MAX) { - generateWarning(ctx, QLatin1String("Index out of range during length set")); - return QV4::Value::undefinedValue(); - } - /* Read the sequence from the QObject property if we're a reference */ - if (This->m_isReference) { - if (!This->m_object) - return QV4::Value::undefinedValue(); - This->loadReference(); - } - /* Determine whether we need to modify the sequence */ - qint32 newCount = static_cast(newLength); - qint32 count = This->m_container.count(); - if (newCount == count) { - return QV4::Value::undefinedValue(); - } else if (newCount > count) { - /* according to ECMA262r3 we need to insert */ - /* undefined values increasing length to newLength. */ - /* We cannot, so we insert default-values instead. */ - This->m_container.reserve(newCount); - while (newCount > count++) { - This->m_container.append(typename Container::value_type()); - } - } else { - /* according to ECMA262r3 we need to remove */ - /* elements until the sequence is the required length. */ - while (newCount < count) { - count--; - This->m_container.removeAt(count); - } - } - /* write back if required. */ - if (This->m_isReference) { - /* write back. already checked that object is non-null, so skip that check here. */ - This->storeReference(); - } - return QV4::Value::undefinedValue(); - } - - QVariant toVariant() const - { return QVariant::fromValue(m_container); } - - static QVariant toVariant(QV4::ArrayObject *array) - { - Container result; - uint32_t length = array->arrayLength(); - for (uint32_t i = 0; i < length; ++i) - result << convertValueToElement(array->getIndexed(i)); - return QVariant::fromValue(result); - } - -private: - void loadReference() const - { - Q_ASSERT(m_object); - Q_ASSERT(m_isReference); - void *a[] = { &m_container, 0 }; - QMetaObject::metacall(m_object, QMetaObject::ReadProperty, m_propertyIndex, a); - } - - void storeReference() - { - Q_ASSERT(m_object); - Q_ASSERT(m_isReference); - int status = -1; - QQmlPropertyPrivate::WriteFlags flags = QQmlPropertyPrivate::DontRemoveBinding; - void *a[] = { &m_container, 0, &status, &flags }; - QMetaObject::metacall(m_object, QMetaObject::WriteProperty, m_propertyIndex, a); - } - - mutable Container m_container; - QPointer m_object; - int m_propertyIndex; - bool m_isReference; - - static QV4::Value getIndexed(QV4::Managed *that, uint index, bool *hasProperty) - { return static_cast *>(that)->containerGetIndexed(index, hasProperty); } - static void putIndexed(Managed *that, uint index, const QV4::Value &value) - { static_cast *>(that)->containerPutIndexed(index, value); } - static QV4::PropertyAttributes queryIndexed(const QV4::Managed *that, uint index) - { return static_cast *>(that)->containerQueryIndexed(index); } - static bool deleteIndexedProperty(QV4::Managed *that, uint index) - { return static_cast *>(that)->containerDeleteIndexedProperty(index); } - static bool isEqualTo(Managed *that, Managed *other) - { return static_cast *>(that)->containerIsEqualTo(other); } - static Property *advanceIterator(Managed *that, ObjectIterator *it, String **name, uint *index, PropertyAttributes *attrs) - { return static_cast *>(that)->containerAdvanceIterator(it, name, index, attrs); } - - static void destroy(Managed *that) - { - static_cast *>(that)->~QQmlSequence(); - } -}; - -typedef QQmlSequence QQmlQStringList; -template<> -DEFINE_MANAGED_VTABLE(QQmlQStringList); -typedef QQmlSequence > QQmlStringList; -template<> -DEFINE_MANAGED_VTABLE(QQmlStringList); -typedef QQmlSequence > QQmlIntList; -template<> -DEFINE_MANAGED_VTABLE(QQmlIntList); -typedef QQmlSequence > QQmlUrlList; -template<> -DEFINE_MANAGED_VTABLE(QQmlUrlList); -typedef QQmlSequence > QQmlBoolList; -template<> -DEFINE_MANAGED_VTABLE(QQmlBoolList); -typedef QQmlSequence > QQmlRealList; -template<> -DEFINE_MANAGED_VTABLE(QQmlRealList); - -#define REGISTER_QML_SEQUENCE_METATYPE(unused, unused2, SequenceType, unused3) qRegisterMetaType(#SequenceType); -SequencePrototype::SequencePrototype(ExecutionEngine *engine) - : QV4::Object(engine) -{ - prototype = engine->arrayPrototype; - FOREACH_QML_SEQUENCE_TYPE(REGISTER_QML_SEQUENCE_METATYPE) -} -#undef REGISTER_QML_SEQUENCE_METATYPE - -void SequencePrototype::init(QV4::ExecutionEngine *engine) -{ - defineDefaultProperty(engine, QStringLiteral("sort"), method_sort, 1); - defineDefaultProperty(engine, QStringLiteral("valueOf"), method_valueOf, 0); -} - -QV4::Value SequencePrototype::method_sort(QV4::SimpleCallContext *ctx) -{ - QV4::Object *o = ctx->thisObject.asObject(); - if (!o || !o->isListType()) - ctx->throwTypeError(); - - if (ctx->argumentCount >= 2) - return ctx->thisObject; - -#define CALL_SORT(SequenceElementType, SequenceElementTypeName, SequenceType, DefaultValue) \ - if (QQml##SequenceElementTypeName##List *s = o->as()) { \ - s->sort(ctx); \ - } else - - FOREACH_QML_SEQUENCE_TYPE(CALL_SORT) - -#undef CALL_SORT - return ctx->thisObject; -} - -#define IS_SEQUENCE(unused1, unused2, SequenceType, unused3) \ - if (sequenceTypeId == qMetaTypeId()) { \ - return true; \ - } else - -bool SequencePrototype::isSequenceType(int sequenceTypeId) -{ - FOREACH_QML_SEQUENCE_TYPE(IS_SEQUENCE) { /* else */ return false; } -} -#undef IS_SEQUENCE - -#define NEW_REFERENCE_SEQUENCE(ElementType, ElementTypeName, SequenceType, unused) \ - if (sequenceType == qMetaTypeId()) { \ - QV4::Object *obj = new (engine->memoryManager) QQml##ElementTypeName##List(engine, object, propertyIndex); \ - return QV4::Value::fromObject(obj); \ - } else - -QV4::Value SequencePrototype::newSequence(QV4::ExecutionEngine *engine, int sequenceType, QObject *object, int propertyIndex, bool *succeeded) -{ - // This function is called when the property is a QObject Q_PROPERTY of - // the given sequence type. Internally we store a typed-sequence - // (as well as object ptr + property index for updated-read and write-back) - // and so access/mutate avoids variant conversion. - *succeeded = true; - FOREACH_QML_SEQUENCE_TYPE(NEW_REFERENCE_SEQUENCE) { /* else */ *succeeded = false; return QV4::Value::undefinedValue(); } -} -#undef NEW_REFERENCE_SEQUENCE - -#define NEW_COPY_SEQUENCE(ElementType, ElementTypeName, SequenceType, unused) \ - if (sequenceType == qMetaTypeId()) { \ - QV4::Object *obj = new (engine->memoryManager) QQml##ElementTypeName##List(engine, v.value()); \ - return QV4::Value::fromObject(obj); \ - } else - -QV4::Value SequencePrototype::fromVariant(QV4::ExecutionEngine *engine, const QVariant& v, bool *succeeded) -{ - // This function is called when assigning a sequence value to a normal JS var - // in a JS block. Internally, we store a sequence of the specified type. - // Access and mutation is extremely fast since it will not need to modify any - // QObject property. - int sequenceType = v.userType(); - *succeeded = true; - FOREACH_QML_SEQUENCE_TYPE(NEW_COPY_SEQUENCE) { /* else */ *succeeded = false; return QV4::Value::undefinedValue(); } -} -#undef NEW_COPY_SEQUENCE - -#define SEQUENCE_TO_VARIANT(ElementType, ElementTypeName, SequenceType, unused) \ - if (QQml##ElementTypeName##List *list = object->as()) \ - return list->toVariant(); \ - else - -QVariant SequencePrototype::toVariant(QV4::Object *object) -{ - Q_ASSERT(object->isListType()); - FOREACH_QML_SEQUENCE_TYPE(SEQUENCE_TO_VARIANT) { /* else */ return QVariant(); } -} - -#define SEQUENCE_TO_VARIANT(ElementType, ElementTypeName, SequenceType, unused) \ - if (typeHint == qMetaTypeId()) { \ - return QQml##ElementTypeName##List::toVariant(a); \ - } else - -QVariant SequencePrototype::toVariant(const QV4::Value &array, int typeHint, bool *succeeded) -{ - *succeeded = true; - - QV4::ArrayObject *a = array.asArrayObject(); - if (!a) { - *succeeded = false; - return QVariant(); - } - FOREACH_QML_SEQUENCE_TYPE(SEQUENCE_TO_VARIANT) { /* else */ *succeeded = false; return QVariant(); } -} - -#undef SEQUENCE_TO_VARIANT - -#define MAP_META_TYPE(ElementType, ElementTypeName, SequenceType, unused) \ - if (object->as()) { \ - return qMetaTypeId(); \ - } else - -int SequencePrototype::metaTypeForSequence(QV4::Object *object) -{ - FOREACH_QML_SEQUENCE_TYPE(MAP_META_TYPE) - /*else*/ { - return -1; - } -} - -#undef MAP_META_TYPE - -QT_END_NAMESPACE diff --git a/src/qml/qml/v4/qv4sequenceobject_p.h b/src/qml/qml/v4/qv4sequenceobject_p.h deleted file mode 100644 index 2cade45092..0000000000 --- a/src/qml/qml/v4/qv4sequenceobject_p.h +++ /dev/null @@ -1,91 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QV4SEQUENCEWRAPPER_P_H -#define QV4SEQUENCEWRAPPER_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include -#include - -#include "qv4value_p.h" -#include "qv4object_p.h" - -QT_BEGIN_NAMESPACE - -namespace QV4 { - -struct SequencePrototype : public QV4::Object -{ - SequencePrototype(QV4::ExecutionEngine *engine); - - void init(QV4::ExecutionEngine *engine); - - static QV4::Value method_valueOf(QV4::SimpleCallContext *ctx) - { - return QV4::Value::fromString(ctx->thisObject.toString(ctx)); - } - - static QV4::Value method_sort(QV4::SimpleCallContext *ctx); - - static bool isSequenceType(int sequenceTypeId); - static QV4::Value newSequence(QV4::ExecutionEngine *engine, int sequenceTypeId, QObject *object, int propertyIndex, bool *succeeded); - static QV4::Value fromVariant(QV4::ExecutionEngine *engine, const QVariant& v, bool *succeeded); - static int metaTypeForSequence(QV4::Object *object); - static QVariant toVariant(QV4::Object *object); - static QVariant toVariant(const QV4::Value &array, int typeHint, bool *succeeded); -}; - -} - -QT_END_NAMESPACE - -#endif // QV4SEQUENCEWRAPPER_P_H diff --git a/src/qml/qml/v4/qv4serialize.cpp b/src/qml/qml/v4/qv4serialize.cpp deleted file mode 100644 index f7389dc6d7..0000000000 --- a/src/qml/qml/v4/qv4serialize.cpp +++ /dev/null @@ -1,399 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4serialize_p.h" - -#include -#include -#include - -#include -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE - -using namespace QV4; - -// We allow the following JavaScript types to be passed between the main and -// the secondary thread: -// + undefined -// + null -// + Boolean -// + String -// + Function -// + Array -// + "Simple" Objects -// + Number -// + Date -// + RegExp -// - -enum Type { - WorkerUndefined, - WorkerNull, - WorkerTrue, - WorkerFalse, - WorkerString, - WorkerFunction, - WorkerArray, - WorkerObject, - WorkerInt32, - WorkerUint32, - WorkerNumber, - WorkerDate, - WorkerRegexp, - WorkerListModel, - WorkerSequence -}; - -static inline quint32 valueheader(Type type, quint32 size = 0) -{ - return quint8(type) << 24 | (size & 0xFFFFFF); -} - -static inline Type headertype(quint32 header) -{ - return (Type)(header >> 24); -} - -static inline quint32 headersize(quint32 header) -{ - return header & 0xFFFFFF; -} - -static inline void push(QByteArray &data, quint32 value) -{ - data.append((const char *)&value, sizeof(quint32)); -} - -static inline void push(QByteArray &data, double value) -{ - data.append((const char *)&value, sizeof(double)); -} - -static inline void push(QByteArray &data, void *ptr) -{ - data.append((const char *)&ptr, sizeof(void *)); -} - -static inline void reserve(QByteArray &data, int extra) -{ - data.reserve(data.size() + extra); -} - -static inline quint32 popUint32(const char *&data) -{ - quint32 rv = *((quint32 *)data); - data += sizeof(quint32); - return rv; -} - -static inline double popDouble(const char *&data) -{ - double rv = *((double *)data); - data += sizeof(double); - return rv; -} - -static inline void *popPtr(const char *&data) -{ - void *rv = *((void **)data); - data += sizeof(void *); - return rv; -} - -// XXX TODO: Check that worker script is exception safe in the case of -// serialization/deserialization failures - -#define ALIGN(size) (((size) + 3) & ~3) -void Serialize::serialize(QByteArray &data, const QV4::Value &v, QV8Engine *engine) -{ - QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine); - if (v.isEmpty()) { - } else if (v.isUndefined()) { - push(data, valueheader(WorkerUndefined)); - } else if (v.isNull()) { - push(data, valueheader(WorkerNull)); - } else if (v.isBoolean()) { - push(data, valueheader(v.booleanValue() == true ? WorkerTrue : WorkerFalse)); - } else if (QV4::String *s = v.asString()) { - const QString &qstr = s->toQString(); - int length = qstr.length(); - if (length > 0xFFFFFF) { - push(data, valueheader(WorkerUndefined)); - return; - } - int utf16size = ALIGN(length * sizeof(uint16_t)); - - reserve(data, utf16size + sizeof(quint32)); - push(data, valueheader(WorkerString, length)); - - int offset = data.size(); - data.resize(data.size() + utf16size); - char *buffer = data.data() + offset; - - memcpy(buffer, qstr.constData(), length*sizeof(QChar)); - } else if (v.asFunctionObject()) { - // XXX TODO: Implement passing function objects between the main and - // worker scripts - push(data, valueheader(WorkerUndefined)); - } else if (QV4::ArrayObject *array = v.asArrayObject()) { - uint32_t length = array->arrayLength(); - if (length > 0xFFFFFF) { - push(data, valueheader(WorkerUndefined)); - return; - } - reserve(data, sizeof(quint32) + length * sizeof(quint32)); - push(data, valueheader(WorkerArray, length)); - for (uint32_t ii = 0; ii < length; ++ii) - serialize(data, array->getIndexed(ii), engine); - } else if (v.isInteger()) { - reserve(data, 2 * sizeof(quint32)); - push(data, valueheader(WorkerInt32)); - push(data, (quint32)v.integerValue()); -// } else if (v->IsUint32()) { -// reserve(data, 2 * sizeof(quint32)); -// push(data, valueheader(WorkerUint32)); -// push(data, v->Uint32Value()); - } else if (v.isNumber()) { - reserve(data, sizeof(quint32) + sizeof(double)); - push(data, valueheader(WorkerNumber)); - push(data, v.asDouble()); - } else if (QV4::DateObject *d = v.asDateObject()) { - reserve(data, sizeof(quint32) + sizeof(double)); - push(data, valueheader(WorkerDate)); - push(data, d->value.asDouble()); - } else if (QV4::RegExpObject *re = v.as()) { - quint32 flags = re->flags(); - QString pattern = re->source(); - int length = pattern.length() + 1; - if (length > 0xFFFFFF) { - push(data, valueheader(WorkerUndefined)); - return; - } - int utf16size = ALIGN(length * sizeof(uint16_t)); - - reserve(data, sizeof(quint32) + utf16size); - push(data, valueheader(WorkerRegexp, flags)); - push(data, (quint32)length); - - int offset = data.size(); - data.resize(data.size() + utf16size); - char *buffer = data.data() + offset; - - memcpy(buffer, pattern.constData(), length*sizeof(QChar)); - } else if (QV4::QObjectWrapper *qobjectWrapper = v.as()) { - // XXX TODO: Generalize passing objects between the main thread and worker scripts so - // that others can trivially plug in their elements. - QQmlListModel *lm = qobject_cast(qobjectWrapper->object()); - if (lm && lm->agent()) { - QQmlListModelWorkerAgent *agent = lm->agent(); - agent->addref(); - push(data, valueheader(WorkerListModel)); - push(data, (void *)agent); - return; - } - // No other QObject's are allowed to be sent - push(data, valueheader(WorkerUndefined)); - } else if (QV4::Object *o = v.asObject()) { - - if (o->isListType()) { - // valid sequence. we generate a length (sequence length + 1 for the sequence type) - uint32_t seqLength = o->get(v4->id_length).toUInt32(); - uint32_t length = seqLength + 1; - if (length > 0xFFFFFF) { - push(data, valueheader(WorkerUndefined)); - return; - } - reserve(data, sizeof(quint32) + length * sizeof(quint32)); - push(data, valueheader(WorkerSequence, length)); - serialize(data, QV4::Value::fromInt32(QV4::SequencePrototype::metaTypeForSequence(o)), engine); // sequence type - for (uint32_t ii = 0; ii < seqLength; ++ii) - serialize(data, o->getIndexed(ii), engine); // sequence elements - - return; - } - - // regular object - QV4::ArrayObject *properties = QV4::ObjectPrototype::getOwnPropertyNames(v4, v); - quint32 length = properties->arrayLength(); - if (length > 0xFFFFFF) { - push(data, valueheader(WorkerUndefined)); - return; - } - push(data, valueheader(WorkerObject, length)); - - QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine); - for (quint32 ii = 0; ii < length; ++ii) { - QV4::String *s = properties->getIndexed(ii).asString(); - serialize(data, QV4::Value::fromString(s), engine); - - bool hasCaught = false; - QV4::ExecutionContext *ctx = v4->current; - QV4::Value val = QV4::Value::undefinedValue(); - try { - val = o->get(s); - } catch (QV4::Exception &e) { - e.accept(ctx); - } - - serialize(data, val, engine); - } - return; - } else { - push(data, valueheader(WorkerUndefined)); - } -} - -QV4::Value Serialize::deserialize(const char *&data, QV8Engine *engine) -{ - quint32 header = popUint32(data); - Type type = headertype(header); - - QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine); - - switch (type) { - case WorkerUndefined: - return QV4::Value::undefinedValue(); - case WorkerNull: - return QV4::Value::nullValue(); - case WorkerTrue: - return QV4::Value::fromBoolean(true); - case WorkerFalse: - return QV4::Value::fromBoolean(false); - case WorkerString: - { - quint32 size = headersize(header); - QString qstr((QChar *)data, size); - data += ALIGN(size * sizeof(uint16_t)); - return QV4::Value::fromString(v4->newString(qstr)); - } - case WorkerFunction: - Q_ASSERT(!"Unreachable"); - break; - case WorkerArray: - { - quint32 size = headersize(header); - QV4::ArrayObject *a = v4->newArrayObject(); - for (quint32 ii = 0; ii < size; ++ii) { - a->putIndexed(ii, deserialize(data, engine)); - } - return QV4::Value::fromObject(a); - } - case WorkerObject: - { - quint32 size = headersize(header); - QV4::Object *o = v4->newObject(); - for (quint32 ii = 0; ii < size; ++ii) { - QV4::Value name = deserialize(data, engine); - QV4::Value value = deserialize(data, engine); - o->put(name.asString(), value); - } - return QV4::Value::fromObject(o); - } - case WorkerInt32: - return QV4::Value::fromInt32((qint32)popUint32(data)); - case WorkerUint32: - return QV4::Value::fromUInt32(popUint32(data)); - case WorkerNumber: - return QV4::Value::fromDouble(popDouble(data)); - case WorkerDate: - return QV4::Value::fromObject(v4->newDateObject(QV4::Value::fromDouble(popDouble(data)))); - case WorkerRegexp: - { - quint32 flags = headersize(header); - quint32 length = popUint32(data); - QString pattern = QString((QChar *)data, length - 1); - data += ALIGN(length * sizeof(uint16_t)); - return QV4::Value::fromObject(v4->newRegExpObject(pattern, flags)); - } - case WorkerListModel: - { - void *ptr = popPtr(data); - QQmlListModelWorkerAgent *agent = (QQmlListModelWorkerAgent *)ptr; - QV4::Value rv = QV4::QObjectWrapper::wrap(v4, agent); - // ### Find a better solution then the ugly property - QQmlListModelWorkerAgent::VariantRef ref(agent); - QVariant var = qVariantFromValue(ref); - rv.asObject()->defineReadonlyProperty(v4->newString("__qml:hidden:ref"), engine->fromVariant(var)); - - agent->release(); - agent->setV8Engine(engine); - return rv; - } - case WorkerSequence: - { - bool succeeded = false; - quint32 length = headersize(header); - quint32 seqLength = length - 1; - int sequenceType = deserialize(data, engine).integerValue(); - QV4::ArrayObject *array = v4->newArrayObject(); - array->arrayReserve(seqLength); - array->arrayDataLen = seqLength; - for (quint32 ii = 0; ii < seqLength; ++ii) - array->arrayData[ii].value = deserialize(data, engine); - array->setArrayLengthUnchecked(seqLength); - QVariant seqVariant = QV4::SequencePrototype::toVariant(QV4::Value::fromObject(array), sequenceType, &succeeded); - return QV4::SequencePrototype::fromVariant(v4, seqVariant, &succeeded); - } - } - Q_ASSERT(!"Unreachable"); - return QV4::Value::undefinedValue(); -} - -QByteArray Serialize::serialize(const QV4::Value &value, QV8Engine *engine) -{ - QByteArray rv; - serialize(rv, value, engine); - return rv; -} - -QV4::Value Serialize::deserialize(const QByteArray &data, QV8Engine *engine) -{ - const char *stream = data.constData(); - return deserialize(stream, engine); -} - -QT_END_NAMESPACE - diff --git a/src/qml/qml/v4/qv4serialize_p.h b/src/qml/qml/v4/qv4serialize_p.h deleted file mode 100644 index 5a04c9d25f..0000000000 --- a/src/qml/qml/v4/qv4serialize_p.h +++ /dev/null @@ -1,80 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QV4SERIALIZE_P_H -#define QV4SERIALIZE_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include -#include - -QT_BEGIN_NAMESPACE - -class QV8Engine; - -namespace QV4 { - -class Serialize { -public: - - static QByteArray serialize(const Value &, QV8Engine *); - static Value deserialize(const QByteArray &, QV8Engine *); - -private: - static void serialize(QByteArray &, const Value &, QV8Engine *); - static Value deserialize(const char *&, QV8Engine *); -}; - -} - -QT_END_NAMESPACE - -#endif // QV8WORKER_P_H diff --git a/src/qml/qml/v4/qv4sparsearray.cpp b/src/qml/qml/v4/qv4sparsearray.cpp deleted file mode 100644 index 835a0d004f..0000000000 --- a/src/qml/qml/v4/qv4sparsearray.cpp +++ /dev/null @@ -1,459 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4sparsearray_p.h" -#include "qv4runtime_p.h" -#include "qv4object_p.h" -#include "qv4functionobject_p.h" -#include - -#ifdef QT_QMAP_DEBUG -# include -# include -#endif - -using namespace QV4; - -bool ArrayElementLessThan::operator()(const Property &p1, const Property &p2) const -{ - Value v1 = p1.value; - Value v2 = p2.value; - - if (v1.isUndefined()) - return false; - if (v2.isUndefined()) - return true; - if (!m_comparefn.isUndefined()) { - Value args[] = { v1, v2 }; - Value result = Value::undefinedValue(); - __qmljs_call_value(m_context, &result, /*thisObject*/0, m_comparefn, args, 2); - return result.toNumber() <= 0; - } - return v1.toString(m_context)->toQString() < v2.toString(m_context)->toQString(); -} - - -const SparseArrayNode *SparseArrayNode::nextNode() const -{ - const SparseArrayNode *n = this; - if (n->right) { - n = n->right; - while (n->left) - n = n->left; - } else { - const SparseArrayNode *y = n->parent(); - while (y && n == y->right) { - n = y; - y = n->parent(); - } - n = y; - } - return n; -} - -const SparseArrayNode *SparseArrayNode::previousNode() const -{ - const SparseArrayNode *n = this; - if (n->left) { - n = n->left; - while (n->right) - n = n->right; - } else { - const SparseArrayNode *y = n->parent(); - while (y && n == y->left) { - n = y; - y = n->parent(); - } - n = y; - } - return n; -} - -SparseArrayNode *SparseArrayNode::copy(SparseArray *d) const -{ - SparseArrayNode *n = d->createNode(size_left, 0, false); - n->value = value; - n->setColor(color()); - if (left) { - n->left = left->copy(d); - n->left->setParent(n); - } else { - n->left = 0; - } - if (right) { - n->right = right->copy(d); - n->right->setParent(n); - } else { - n->right = 0; - } - return n; -} - -/* - x y - \ / \ - y --> x b - / \ \ - a b a -*/ -void SparseArray::rotateLeft(SparseArrayNode *x) -{ - SparseArrayNode *&root = header.left; - SparseArrayNode *y = x->right; - x->right = y->left; - if (y->left != 0) - y->left->setParent(x); - y->setParent(x->parent()); - if (x == root) - root = y; - else if (x == x->parent()->left) - x->parent()->left = y; - else - x->parent()->right = y; - y->left = x; - x->setParent(y); - y->size_left += x->size_left; -} - - -/* - x y - / / \ - y --> a x - / \ / - a b b -*/ -void SparseArray::rotateRight(SparseArrayNode *x) -{ - SparseArrayNode *&root = header.left; - SparseArrayNode *y = x->left; - x->left = y->right; - if (y->right != 0) - y->right->setParent(x); - y->setParent(x->parent()); - if (x == root) - root = y; - else if (x == x->parent()->right) - x->parent()->right = y; - else - x->parent()->left = y; - y->right = x; - x->setParent(y); - x->size_left -= y->size_left; -} - - -void SparseArray::rebalance(SparseArrayNode *x) -{ - SparseArrayNode *&root = header.left; - x->setColor(SparseArrayNode::Red); - while (x != root && x->parent()->color() == SparseArrayNode::Red) { - if (x->parent() == x->parent()->parent()->left) { - SparseArrayNode *y = x->parent()->parent()->right; - if (y && y->color() == SparseArrayNode::Red) { - x->parent()->setColor(SparseArrayNode::Black); - y->setColor(SparseArrayNode::Black); - x->parent()->parent()->setColor(SparseArrayNode::Red); - x = x->parent()->parent(); - } else { - if (x == x->parent()->right) { - x = x->parent(); - rotateLeft(x); - } - x->parent()->setColor(SparseArrayNode::Black); - x->parent()->parent()->setColor(SparseArrayNode::Red); - rotateRight (x->parent()->parent()); - } - } else { - SparseArrayNode *y = x->parent()->parent()->left; - if (y && y->color() == SparseArrayNode::Red) { - x->parent()->setColor(SparseArrayNode::Black); - y->setColor(SparseArrayNode::Black); - x->parent()->parent()->setColor(SparseArrayNode::Red); - x = x->parent()->parent(); - } else { - if (x == x->parent()->left) { - x = x->parent(); - rotateRight(x); - } - x->parent()->setColor(SparseArrayNode::Black); - x->parent()->parent()->setColor(SparseArrayNode::Red); - rotateLeft(x->parent()->parent()); - } - } - } - root->setColor(SparseArrayNode::Black); -} - -void SparseArray::deleteNode(SparseArrayNode *z) -{ - SparseArrayNode *&root = header.left; - SparseArrayNode *y = z; - SparseArrayNode *x; - SparseArrayNode *x_parent; - if (y->left == 0) { - x = y->right; - if (y == mostLeftNode) { - if (x) - mostLeftNode = x; // It cannot have (left) children due the red black invariant. - else - mostLeftNode = y->parent(); - } - } else { - if (y->right == 0) { - x = y->left; - } else { - y = y->right; - while (y->left != 0) - y = y->left; - x = y->right; - } - } - if (y != z) { - z->left->setParent(y); - y->left = z->left; - if (y != z->right) { - x_parent = y->parent(); - if (x) - x->setParent(y->parent()); - y->parent()->left = x; - y->right = z->right; - z->right->setParent(y); - } else { - x_parent = y; - } - if (root == z) - root = y; - else if (z->parent()->left == z) - z->parent()->left = y; - else - z->parent()->right = y; - y->setParent(z->parent()); - // Swap the colors - SparseArrayNode::Color c = y->color(); - y->setColor(z->color()); - z->setColor(c); - y = z; - } else { - x_parent = y->parent(); - if (x) - x->setParent(y->parent()); - if (root == z) - root = x; - else if (z->parent()->left == z) - z->parent()->left = x; - else - z->parent()->right = x; - } - if (y->color() != SparseArrayNode::Red) { - while (x != root && (x == 0 || x->color() == SparseArrayNode::Black)) { - if (x == x_parent->left) { - SparseArrayNode *w = x_parent->right; - if (w->color() == SparseArrayNode::Red) { - w->setColor(SparseArrayNode::Black); - x_parent->setColor(SparseArrayNode::Red); - rotateLeft(x_parent); - w = x_parent->right; - } - if ((w->left == 0 || w->left->color() == SparseArrayNode::Black) && - (w->right == 0 || w->right->color() == SparseArrayNode::Black)) { - w->setColor(SparseArrayNode::Red); - x = x_parent; - x_parent = x_parent->parent(); - } else { - if (w->right == 0 || w->right->color() == SparseArrayNode::Black) { - if (w->left) - w->left->setColor(SparseArrayNode::Black); - w->setColor(SparseArrayNode::Red); - rotateRight(w); - w = x_parent->right; - } - w->setColor(x_parent->color()); - x_parent->setColor(SparseArrayNode::Black); - if (w->right) - w->right->setColor(SparseArrayNode::Black); - rotateLeft(x_parent); - break; - } - } else { - SparseArrayNode *w = x_parent->left; - if (w->color() == SparseArrayNode::Red) { - w->setColor(SparseArrayNode::Black); - x_parent->setColor(SparseArrayNode::Red); - rotateRight(x_parent); - w = x_parent->left; - } - if ((w->right == 0 || w->right->color() == SparseArrayNode::Black) && - (w->left == 0 || w->left->color() == SparseArrayNode::Black)) { - w->setColor(SparseArrayNode::Red); - x = x_parent; - x_parent = x_parent->parent(); - } else { - if (w->left == 0 || w->left->color() == SparseArrayNode::Black) { - if (w->right) - w->right->setColor(SparseArrayNode::Black); - w->setColor(SparseArrayNode::Red); - rotateLeft(w); - w = x_parent->left; - } - w->setColor(x_parent->color()); - x_parent->setColor(SparseArrayNode::Black); - if (w->left) - w->left->setColor(SparseArrayNode::Black); - rotateRight(x_parent); - break; - } - } - } - if (x) - x->setColor(SparseArrayNode::Black); - } - free(y); - --numEntries; -} - -void SparseArray::recalcMostLeftNode() -{ - mostLeftNode = &header; - while (mostLeftNode->left) - mostLeftNode = mostLeftNode->left; -} - -static inline int qMapAlignmentThreshold() -{ - // malloc on 32-bit platforms should return pointers that are 8-byte - // aligned or more while on 64-bit platforms they should be 16-byte aligned - // or more - return 2 * sizeof(void*); -} - -static inline void *qMapAllocate(int alloc, int alignment) -{ - return alignment > qMapAlignmentThreshold() - ? qMallocAligned(alloc, alignment) - : ::malloc(alloc); -} - -static inline void qMapDeallocate(SparseArrayNode *node, int alignment) -{ - if (alignment > qMapAlignmentThreshold()) - qFreeAligned(node); - else - ::free(node); -} - -SparseArrayNode *SparseArray::createNode(uint sl, SparseArrayNode *parent, bool left) -{ - SparseArrayNode *node = static_cast(qMapAllocate(sizeof(SparseArrayNode), Q_ALIGNOF(SparseArrayNode))); - Q_CHECK_PTR(node); - - node->p = (quintptr)parent; - node->left = 0; - node->right = 0; - node->size_left = sl; - node->value = UINT_MAX; - ++numEntries; - - if (parent) { - if (left) { - parent->left = node; - if (parent == mostLeftNode) - mostLeftNode = node; - } else { - parent->right = node; - } - node->setParent(parent); - rebalance(node); - } - return node; -} - -void SparseArray::freeTree(SparseArrayNode *root, int alignment) -{ - if (root->left) - freeTree(root->left, alignment); - if (root->right) - freeTree(root->right, alignment); - qMapDeallocate(root, alignment); -} - -SparseArray::SparseArray() - : numEntries(0) -{ - header.p = 0; - header.left = 0; - header.right = 0; - mostLeftNode = &header; -} - -SparseArray::SparseArray(const SparseArray &other) -{ - header.p = 0; - header.right = 0; - if (other.header.left) { - header.left = other.header.left->copy(this); - header.left->setParent(&header); - recalcMostLeftNode(); - } -} - -SparseArrayNode *SparseArray::insert(uint akey) -{ - SparseArrayNode *n = root(); - SparseArrayNode *y = end(); - bool left = true; - uint s = akey; - while (n) { - y = n; - if (s == n->size_left) { - return n; - } else if (s < n->size_left) { - left = true; - n = n->left; - } else { - left = false; - s -= n->size_left; - n = n->right; - } - } - - return createNode(s, y, left); -} diff --git a/src/qml/qml/v4/qv4sparsearray_p.h b/src/qml/qml/v4/qv4sparsearray_p.h deleted file mode 100644 index 384d2ef045..0000000000 --- a/src/qml/qml/v4/qv4sparsearray_p.h +++ /dev/null @@ -1,367 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QV4ARRAY_H -#define QV4ARRAY_H - -#include "qv4global_p.h" -#include -#include "qv4value_p.h" -#include "qv4property_p.h" -#include - -#ifdef Q_MAP_DEBUG -#include -#endif - -#include - -QT_BEGIN_NAMESPACE - -namespace QV4 { - -struct SparseArray; - -class ArrayElementLessThan -{ -public: - inline ArrayElementLessThan(ExecutionContext *context, Object *thisObject, const Value &comparefn) - : m_context(context), thisObject(thisObject), m_comparefn(comparefn) {} - - bool operator()(const Property &v1, const Property &v2) const; - -private: - ExecutionContext *m_context; - Object *thisObject; - Value m_comparefn; -}; - - -struct SparseArrayNode -{ - quintptr p; - SparseArrayNode *left; - SparseArrayNode *right; - uint size_left; - uint value; - - enum Color { Red = 0, Black = 1 }; - enum { Mask = 3 }; // reserve the second bit as well - - const SparseArrayNode *nextNode() const; - SparseArrayNode *nextNode() { return const_cast(const_cast(this)->nextNode()); } - const SparseArrayNode *previousNode() const; - SparseArrayNode *previousNode() { return const_cast(const_cast(this)->previousNode()); } - - Color color() const { return Color(p & 1); } - void setColor(Color c) { if (c == Black) p |= Black; else p &= ~Black; } - SparseArrayNode *parent() const { return reinterpret_cast(p & ~Mask); } - void setParent(SparseArrayNode *pp) { p = (p & Mask) | quintptr(pp); } - - uint key() const { - uint k = size_left; - const SparseArrayNode *n = this; - while (SparseArrayNode *p = n->parent()) { - if (p && p->right == n) - k += p->size_left; - n = p; - } - return k; - } - - SparseArrayNode *copy(SparseArray *d) const; - - SparseArrayNode *lowerBound(uint key); - SparseArrayNode *upperBound(uint key); -}; - - -inline SparseArrayNode *SparseArrayNode::lowerBound(uint akey) -{ - SparseArrayNode *n = this; - SparseArrayNode *last = 0; - while (n) { - if (akey <= n->size_left) { - last = n; - n = n->left; - } else { - akey -= n->size_left; - n = n->right; - } - } - return last; -} - - -inline SparseArrayNode *SparseArrayNode::upperBound(uint akey) -{ - SparseArrayNode *n = this; - SparseArrayNode *last = 0; - while (n) { - if (akey < n->size_left) { - last = n; - n = n->left; - } else { - akey -= n->size_left; - n = n->right; - } - } - return last; -} - - - -struct Q_QML_EXPORT SparseArray -{ - SparseArray(); - ~SparseArray() { - if (root()) - freeTree(header.left, Q_ALIGNOF(SparseArrayNode)); - } - - SparseArray(const SparseArray &other); -private: - SparseArray &operator=(const SparseArray &other); - - int numEntries; - SparseArrayNode header; - SparseArrayNode *mostLeftNode; - - void rotateLeft(SparseArrayNode *x); - void rotateRight(SparseArrayNode *x); - void rebalance(SparseArrayNode *x); - void recalcMostLeftNode(); - - SparseArrayNode *root() const { return header.left; } - - void deleteNode(SparseArrayNode *z); - - -public: - SparseArrayNode *createNode(uint sl, SparseArrayNode *parent, bool left); - void freeTree(SparseArrayNode *root, int alignment); - - SparseArrayNode *findNode(uint akey) const; - - uint pop_front(); - void push_front(uint at); - uint pop_back(uint len); - void push_back(uint at, uint len); - - QList keys() const; - - const SparseArrayNode *end() const { return &header; } - SparseArrayNode *end() { return &header; } - const SparseArrayNode *begin() const { if (root()) return mostLeftNode; return end(); } - SparseArrayNode *begin() { if (root()) return mostLeftNode; return end(); } - - SparseArrayNode *erase(SparseArrayNode *n); - - SparseArrayNode *lowerBound(uint key); - const SparseArrayNode *lowerBound(uint key) const; - SparseArrayNode *upperBound(uint key); - const SparseArrayNode *upperBound(uint key) const; - SparseArrayNode *insert(uint akey); - - // STL compatibility - typedef uint key_type; - typedef int mapped_type; - typedef qptrdiff difference_type; - typedef int size_type; - -#ifdef Q_MAP_DEBUG - void dump() const; -#endif -}; - -inline SparseArrayNode *SparseArray::findNode(uint akey) const -{ - SparseArrayNode *n = root(); - - while (n) { - if (akey == n->size_left) { - return n; - } else if (akey < n->size_left) { - n = n->left; - } else { - akey -= n->size_left; - n = n->right; - } - } - - return 0; -} - -inline uint SparseArray::pop_front() -{ - uint idx = UINT_MAX ; - - SparseArrayNode *n = findNode(0); - if (n) { - idx = n->value; - deleteNode(n); - // adjust all size_left indices on the path to leftmost item by 1 - SparseArrayNode *n = root(); - while (n) { - n->size_left -= 1; - n = n->left; - } - } - return idx; -} - -inline void SparseArray::push_front(uint value) -{ - // adjust all size_left indices on the path to leftmost item by 1 - SparseArrayNode *n = root(); - while (n) { - n->size_left += 1; - n = n->left; - } - n = insert(0); - n->value = value; -} - -inline uint SparseArray::pop_back(uint len) -{ - uint idx = UINT_MAX; - if (!len) - return idx; - - SparseArrayNode *n = findNode(len - 1); - if (n) { - idx = n->value; - deleteNode(n); - } - return idx; -} - -inline void SparseArray::push_back(uint index, uint len) -{ - SparseArrayNode *n = insert(len); - n->value = index; -} - -#ifdef Q_MAP_DEBUG - -void SparseArray::dump() const -{ - const_iterator it = begin(); - qDebug() << "map dump:"; - while (it != end()) { - const SparseArrayNode *n = it.i; - int depth = 0; - while (n && n != root()) { - ++depth; - n = n->parent(); - } - QByteArray space(4*depth, ' '); - qDebug() << space << (it.i->color() == SparseArrayNode::Red ? "Red " : "Black") << it.i << it.i->left << it.i->right - << it.key() << it.value(); - ++it; - } - qDebug() << "---------"; -} -#endif - - -inline SparseArrayNode *SparseArray::erase(SparseArrayNode *n) -{ - if (n == end()) - return n; - - SparseArrayNode *next = n->nextNode(); - deleteNode(n); - return next; -} - -inline QList SparseArray::keys() const -{ - QList res; - res.reserve(numEntries); - SparseArrayNode *n = mostLeftNode; - while (n != end()) { - res.append(n->key()); - n = n->nextNode(); - } - return res; -} - -inline const SparseArrayNode *SparseArray::lowerBound(uint akey) const -{ - const SparseArrayNode *lb = root()->lowerBound(akey); - if (!lb) - lb = end(); - return lb; -} - - -inline SparseArrayNode *SparseArray::lowerBound(uint akey) -{ - SparseArrayNode *lb = root()->lowerBound(akey); - if (!lb) - lb = end(); - return lb; -} - - -inline const SparseArrayNode *SparseArray::upperBound(uint akey) const -{ - const SparseArrayNode *ub = root()->upperBound(akey); - if (!ub) - ub = end(); - return ub; -} - - -inline SparseArrayNode *SparseArray::upperBound(uint akey) -{ - SparseArrayNode *ub = root()->upperBound(akey); - if (!ub) - ub = end(); - return ub; -} - -} - -QT_END_NAMESPACE - -#endif // QMAP_H diff --git a/src/qml/qml/v4/qv4ssa.cpp b/src/qml/qml/v4/qv4ssa.cpp deleted file mode 100644 index a139ed9fff..0000000000 --- a/src/qml/qml/v4/qv4ssa.cpp +++ /dev/null @@ -1,2122 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4ssa_p.h" -#include "qv4util_p.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef CONST -#undef CONST -#endif - -#define QV4_NO_LIVENESS -#undef SHOW_SSA - -QT_USE_NAMESPACE - -using namespace QQmlJS; -using namespace V4IR; - -namespace { - -QTextStream qout(stdout, QIODevice::WriteOnly); - -void showMeTheCode(Function *function) -{ - static bool showCode = !qgetenv("SHOW_CODE").isNull(); - if (showCode) { - QVector code; - QHash leader; - - foreach (BasicBlock *block, function->basicBlocks) { - if (block->statements.isEmpty()) - continue; - leader.insert(block->statements.first(), block); - foreach (Stmt *s, block->statements) { - code.append(s); - } - } - - QString name; - if (function->name && !function->name->isEmpty()) - name = *function->name; - else - name.sprintf("%p", function); - - qout << "function " << name << "("; - for (int i = 0; i < function->formals.size(); ++i) { - if (i != 0) - qout << ", "; - qout << *function->formals.at(i); - } - qout << ")" << endl - << "{" << endl; - - foreach (const QString *local, function->locals) { - qout << " var " << *local << ';' << endl; - } - - for (int i = 0; i < code.size(); ++i) { - Stmt *s = code.at(i); - - if (BasicBlock *bb = leader.value(s)) { - qout << endl; - QByteArray str; - str.append('L'); - str.append(QByteArray::number(bb->index)); - str.append(':'); - for (int i = 66 - str.length(); i; --i) - str.append(' '); - qout << str; - qout << "// predecessor blocks:"; - foreach (BasicBlock *in, bb->in) - qout << " L" << in->index; - if (bb->in.isEmpty()) - qout << "(none)"; - if (BasicBlock *container = bb->containingGroup()) - qout << "; container block: L" << container->index; - if (bb->isGroupStart()) - qout << "; group start"; - qout << endl; - } - Stmt *n = (i + 1) < code.size() ? code.at(i + 1) : 0; -// if (n && s->asJump() && s->asJump()->target == leader.value(n)) { -// continue; -// } - - QByteArray str; - QBuffer buf(&str); - buf.open(QIODevice::WriteOnly); - QTextStream out(&buf); - if (s->id > 0) - out << s->id << ": "; - s->dump(out, Stmt::MIR); - out.flush(); - - if (s->location.isValid()) - qout << " // line: " << s->location.startLine << " column: " << s->location.startColumn << endl; - -#ifndef QV4_NO_LIVENESS - for (int i = 60 - str.size(); i >= 0; --i) - str.append(' '); - - qout << " " << str; - - // if (! s->uses.isEmpty()) { - // qout << " // uses:"; - // foreach (unsigned use, s->uses) { - // qout << " %" << use; - // } - // } - - // if (! s->defs.isEmpty()) { - // qout << " // defs:"; - // foreach (unsigned def, s->defs) { - // qout << " %" << def; - // } - // } - -# if 0 - if (! s->d->liveIn.isEmpty()) { - qout << " // lives in:"; - for (int i = 0; i < s->d->liveIn.size(); ++i) { - if (s->d->liveIn.testBit(i)) - qout << " %" << i; - } - } -# else - if (! s->d->liveOut.isEmpty()) { - qout << " // lives out:"; - for (int i = 0; i < s->d->liveOut.size(); ++i) { - if (s->d->liveOut.testBit(i)) - qout << " %" << i; - } - } -# endif -#else - qout << " " << str; -#endif - - qout << endl; - - if (n && s->asCJump() /*&& s->asCJump()->iffalse != leader.value(n)*/) { - qout << " else goto L" << s->asCJump()->iffalse->index << ";" << endl; - } - } - - qout << "}" << endl - << endl; - } -} - -class DominatorTree { - int N; - QHash dfnum; - QVector vertex; - QHash parent; - QHash ancestor; - QHash best; - QHash semi; - QHash idom; - QHash samedom; - QHash > bucket; - - void DFS(BasicBlock *p, BasicBlock *n) { - if (dfnum[n] == 0) { - dfnum[n] = N; - vertex[N] = n; - parent[n] = p; - ++N; - foreach (BasicBlock *w, n->out) - DFS(n, w); - } - } - - BasicBlock *ancestorWithLowestSemi(BasicBlock *v) { - BasicBlock *a = ancestor[v]; - if (ancestor[a]) { - BasicBlock *b = ancestorWithLowestSemi(a); - ancestor[v] = ancestor[a]; - if (dfnum[semi[b]] < dfnum[semi[best[v]]]) - best[v] = b; - } - return best[v]; - } - - void link(BasicBlock *p, BasicBlock *n) { - ancestor[n] = p; - best[n] = n; - } - - void calculateIDoms(const QVector &nodes) { - Q_ASSERT(nodes.first()->in.isEmpty()); - vertex.resize(nodes.size()); - foreach (BasicBlock *n, nodes) { - dfnum[n] = 0; - semi[n] = 0; - ancestor[n] = 0; - idom[n] = 0; - samedom[n] = 0; - } - - DFS(0, nodes.first()); - Q_ASSERT(N == nodes.size()); // fails with unreachable nodes... - - for (int i = N - 1; i > 0; --i) { - BasicBlock *n = vertex[i]; - BasicBlock *p = parent[n]; - BasicBlock *s = p; - - foreach (BasicBlock *v, n->in) { - BasicBlock *ss; - if (dfnum[v] <= dfnum[n]) - ss = v; - else - ss = semi[ancestorWithLowestSemi(v)]; - if (dfnum[ss] < dfnum[s]) - s = ss; - } - semi[n] = s; - bucket[s].insert(n); - link(p, n); - foreach (BasicBlock *v, bucket[p]) { - BasicBlock *y = ancestorWithLowestSemi(v); - Q_ASSERT(semi[y] == p); - if (semi[y] == semi[v]) - idom[v] = p; - else - samedom[v] = y; - } - bucket[p].clear(); - } - for (int i = 1; i < N; ++i) { - BasicBlock *n = vertex[i]; - Q_ASSERT(ancestor[n] && ((semi[n] && dfnum[ancestor[n]] <= dfnum[semi[n]]) || semi[n] == n)); - Q_ASSERT(bucket[n].isEmpty()); - if (BasicBlock *sdn = samedom[n]) - idom[n] = idom[sdn]; - } - -#ifdef SHOW_SSA - qout << "Immediate dominators:" << endl; - foreach (BasicBlock *to, nodes) { - qout << '\t'; - if (BasicBlock *from = idom.value(to)) - qout << from->index; - else - qout << "(none)"; - qout << " -> " << to->index << endl; - } -#endif // SHOW_SSA - } - - bool dominates(BasicBlock *dominator, BasicBlock *dominated) const { - for (BasicBlock *it = dominated; it; it = idom[it]) { - if (it == dominator) - return true; - } - - return false; - } - - void computeDF(BasicBlock *n) { - if (DF.contains(n)) - return; // TODO: verify this! - - QSet S; - foreach (BasicBlock *y, n->out) - if (idom[y] != n) - S.insert(y); - - /* - * foreach child c of n in the dominator tree - * computeDF[c] - * foreach element w of DF[c] - * if n does not dominate w or if n = w - * S.insert(w) - * DF[n] = S; - */ - foreach (BasicBlock *c, children[n]) { - computeDF(c); - foreach (BasicBlock *w, DF[c]) - if (!dominates(n, w) || n == w) - S.insert(w); - } - DF[n] = S; - -#ifdef SHOW_SSA - qout << "\tDF[" << n->index << "]: {"; - QList SList = S.values(); - for (int i = 0; i < SList.size(); ++i) { - if (i > 0) - qout << ", "; - qout << SList[i]->index; - } - qout << "}" << endl; -#endif // SHOW_SSA -#ifndef QT_NO_DEBUG - foreach (BasicBlock *fBlock, S) { - Q_ASSERT(!dominates(n, fBlock) || fBlock == n); - bool hasDominatedSucc = false; - foreach (BasicBlock *succ, fBlock->in) - if (dominates(n, succ)) - hasDominatedSucc = true; - if (!hasDominatedSucc) { - qout << fBlock->index << " in DF[" << n->index << "] has no dominated predecessors" << endl; - } - Q_ASSERT(hasDominatedSucc); - } -#endif // !QT_NO_DEBUG - } - - QHash > children; - QHash > DF; - -public: - DominatorTree(const QVector &nodes) - : N(0) - { - calculateIDoms(nodes); - - // compute children of n - foreach (BasicBlock *n, nodes) - children[idom[n]].insert(n); - -#ifdef SHOW_SSA - qout << "Dominator Frontiers:" << endl; -#endif // SHOW_SSA - foreach (BasicBlock *n, nodes) - computeDF(n); - } - - QSet operator[](BasicBlock *n) const { - return DF[n]; - } - - BasicBlock *immediateDominator(BasicBlock *bb) const { - return idom[bb]; - } -}; - -class VariableCollector: public StmtVisitor, ExprVisitor { - QHash > _defsites; - QHash > A_orig; - QSet nonLocals; - QSet killed; - - BasicBlock *currentBB; - const bool variablesCanEscape; - bool isCollectable(Temp *t) const - { - switch (t->kind) { - case Temp::Formal: - case Temp::ScopedFormal: - case Temp::ScopedLocal: - return false; - case Temp::Local: - return !variablesCanEscape; - case Temp::VirtualRegister: - return true; - default: - // PhysicalRegister and StackSlot can only get inserted later. - Q_ASSERT(!"Invalid temp kind!"); - return false; - } - } - -public: - VariableCollector(Function *function) - : variablesCanEscape(function->variablesCanEscape()) - { -#ifdef SHOW_SSA - qout << "Variables collected:" << endl; -#endif // SHOW_SSA - - foreach (BasicBlock *bb, function->basicBlocks) { - currentBB = bb; - killed.clear(); - killed.reserve(bb->statements.size() / 2); - foreach (Stmt *s, bb->statements) { - s->accept(this); - } - } - -#ifdef SHOW_SSA - qout << "Non-locals:" << endl; - foreach (const Temp &nonLocal, nonLocals) { - qout << "\t"; - nonLocal.dump(qout); - qout << endl; - } - - qout << "end collected variables." << endl; -#endif // SHOW_SSA - } - - QList vars() const { - return _defsites.keys(); - } - - QSet defsite(const Temp &n) const { - return _defsites[n]; - } - - QSet inBlock(BasicBlock *n) const { - return A_orig[n]; - } - - bool isNonLocal(const Temp &var) const { return nonLocals.contains(var); } - -protected: - virtual void visitPhi(Phi *) {}; - virtual void visitConvert(Convert *e) { e->expr->accept(this); }; - - virtual void visitConst(Const *) {} - virtual void visitString(String *) {} - virtual void visitRegExp(RegExp *) {} - virtual void visitName(Name *) {} - virtual void visitClosure(Closure *) {} - virtual void visitUnop(Unop *e) { e->expr->accept(this); } - virtual void visitBinop(Binop *e) { e->left->accept(this); e->right->accept(this); } - virtual void visitSubscript(Subscript *e) { e->base->accept(this); e->index->accept(this); } - virtual void visitMember(Member *e) { e->base->accept(this); } - virtual void visitExp(Exp *s) { s->expr->accept(this); } - virtual void visitJump(Jump *) {} - virtual void visitCJump(CJump *s) { s->cond->accept(this); } - virtual void visitRet(Ret *s) { s->expr->accept(this); } - virtual void visitTry(Try *) { // ### TODO - } - - virtual void visitCall(Call *e) { - e->base->accept(this); - for (ExprList *it = e->args; it; it = it->next) - it->expr->accept(this); - } - - virtual void visitNew(New *e) { - e->base->accept(this); - for (ExprList *it = e->args; it; it = it->next) - it->expr->accept(this); - } - - virtual void visitMove(Move *s) { - s->source->accept(this); - - if (Temp *t = s->target->asTemp()) { - if (isCollectable(t)) { -#ifdef SHOW_SSA - qout << '\t'; - t->dump(qout); - qout << " -> L" << currentBB->index << endl; -#endif // SHOW_SSA - - _defsites[*t].insert(currentBB); - A_orig[currentBB].insert(*t); - - // For semi-pruned SSA: - killed.insert(*t); - } - } - } - - virtual void visitTemp(Temp *t) - { - if (isCollectable(t)) - if (!killed.contains(*t)) - nonLocals.insert(*t); - } -}; - -void insertPhiNode(const Temp &a, BasicBlock *y, Function *f) { -#if defined(SHOW_SSA) - qout << "-> inserted phi node for variable "; - a.dump(qout); - qout << " in block " << y->index << endl; -#endif - - Phi *phiNode = f->New(); - phiNode->targetTemp = f->New(); - phiNode->targetTemp->init(a.kind, a.index, 0); - y->statements.prepend(phiNode); - - phiNode->incoming.resize(y->in.size()); - for (int i = 0, ei = y->in.size(); i < ei; ++i) { - Temp *t = f->New(); - t->init(a.kind, a.index, 0); - phiNode->incoming[i] = t; - } -} - -class VariableRenamer: public StmtVisitor, public ExprVisitor -{ - Function *function; - QHash > stack; - QSet seen; - - QHash defCounts; - - const bool variablesCanEscape; - bool isRenamable(Temp *t) const - { - switch (t->kind) { - case Temp::Formal: - case Temp::ScopedFormal: - case Temp::ScopedLocal: - return false; - case Temp::Local: - return !variablesCanEscape; - case Temp::VirtualRegister: - return true; - default: - Q_ASSERT(!"Invalid temp kind!"); - return false; - } - } - int nextFreeTemp() { - const int next = function->tempCount++; -// qDebug()<<"Next free temp:"<variablesCanEscape()) - { - if (!variablesCanEscape) { - Temp t; - t.init(Temp::Local, 0, 0); - for (int i = 0, ei = f->locals.size(); i != ei; ++i) { - t.index = i; - stack[t].push(nextFreeTemp()); - } - } - - Temp t; - t.init(Temp::VirtualRegister, 0, 0); - for (int i = 0, ei = f->tempCount; i != ei; ++i) { - t.index = i; - stack[t].push(i); - } - } - - void run() { - foreach (BasicBlock *n, function->basicBlocks) - rename(n); - -#ifdef SHOW_SSA -// qout << "Temp to local mapping:" << endl; -// foreach (int key, tempMapping.keys()) -// qout << '\t' << key << " -> " << tempMapping[key] << endl; -#endif - } - - void rename(BasicBlock *n) { - if (seen.contains(n)) - return; - seen.insert(n); -// qDebug() << "I: L"<index; - - // [1]: - foreach (Stmt *s, n->statements) - s->accept(this); - - QHash dc = defCounts; - defCounts.clear(); - - // [2]: - foreach (BasicBlock *Y, n->out) { - const int j = Y->in.indexOf(n); - Q_ASSERT(j >= 0 && j < Y->in.size()); - foreach (Stmt *s, Y->statements) { - if (Phi *phi = s->asPhi()) { - Temp *t = phi->incoming[j]->asTemp(); - unsigned newTmp = stack[*t].top(); -// qDebug()<<"I: replacing phi use"<index; - t->index = newTmp; - t->kind = Temp::VirtualRegister; - } else { - break; - } - } - } - - // [3]: - foreach (BasicBlock *X, n->out) - rename(X); - - // [4]: - for (QHash::const_iterator i = dc.begin(), ei = dc.end(); i != ei; ++i) { -// qDebug()< " << i.value(); - for (unsigned j = 0, ej = i.value(); j < ej; ++j) - stack[i.key()].pop(); - } - } - -protected: - virtual void visitTemp(Temp *e) { // only called for uses, not defs - if (isRenamable(e)) { -// qDebug()<<"I: replacing use of"<index<<"with"<index].top(); - e->index = stack[*e].top(); - e->kind = Temp::VirtualRegister; - } - } - - virtual void visitMove(Move *s) { - // uses: - s->source->accept(this); - - // defs: - if (Temp *t = s->target->asTemp()) - renameTemp(t); - else - s->target->accept(this); - } - - void renameTemp(Temp *t) { - if (isRenamable(t)) { - defCounts[*t] = defCounts.value(*t, 0) + 1; - const int newIdx = nextFreeTemp(); - stack[*t].push(newIdx); -// qDebug()<<"I: replacing def of"<kind = Temp::VirtualRegister; - t->index = newIdx; - } - } - - virtual void visitConvert(Convert *e) { e->expr->accept(this); } - virtual void visitPhi(Phi *s) { renameTemp(s->targetTemp); } - - virtual void visitExp(Exp *s) { s->expr->accept(this); } - - virtual void visitJump(Jump *) {} - virtual void visitCJump(CJump *s) { s->cond->accept(this); } - virtual void visitRet(Ret *s) { s->expr->accept(this); } - virtual void visitTry(Try *s) { /* this should never happen */ } - - virtual void visitConst(Const *) {} - virtual void visitString(String *) {} - virtual void visitRegExp(RegExp *) {} - virtual void visitName(Name *) {} - virtual void visitClosure(Closure *) {} - virtual void visitUnop(Unop *e) { e->expr->accept(this); } - virtual void visitBinop(Binop *e) { e->left->accept(this); e->right->accept(this); } - virtual void visitCall(Call *e) { - e->base->accept(this); - for (ExprList *it = e->args; it; it = it->next) - it->expr->accept(this); - } - - virtual void visitNew(New *e) { - e->base->accept(this); - for (ExprList *it = e->args; it; it = it->next) - it->expr->accept(this); - } - - virtual void visitSubscript(Subscript *e) { - e->base->accept(this); - e->index->accept(this); - } - - virtual void visitMember(Member *e) { - e->base->accept(this); - } -}; - -void convertToSSA(Function *function, const DominatorTree &df) -{ -#ifdef SHOW_SSA - qout << "Converting function "; - if (function->name) - qout << *function->name; - else - qout << ""; - qout << " to SSA..." << endl; -#endif // SHOW_SSA - - // Collect all applicable variables: - VariableCollector variables(function); - - // Place phi functions: - QHash > A_phi; - foreach (Temp a, variables.vars()) { - if (!variables.isNonLocal(a)) - continue; // for semi-pruned SSA - - QList W = QList::fromSet(variables.defsite(a)); - while (!W.isEmpty()) { - BasicBlock *n = W.first(); - W.removeFirst(); - foreach (BasicBlock *y, df[n]) { - if (!A_phi[y].contains(a)) { - insertPhiNode(a, y, function); - A_phi[y].insert(a); - if (!variables.inBlock(y).contains(a)) - W.append(y); - } - } - } - } - showMeTheCode(function); - - // Rename variables: - VariableRenamer(function).run(); -} - -class DefUsesCalculator: public StmtVisitor, public ExprVisitor { -public: - struct DefUse { - DefUse() - : defStmt(0) - , blockOfStatement(0) - {} - Stmt *defStmt; - BasicBlock *blockOfStatement; - QList uses; - }; - -private: - const bool _variablesCanEscape; - QHash _defUses; - QHash > _usesPerStatement; - - BasicBlock *_block; - Stmt *_stmt; - - bool isCollectible(Temp *t) const { - switch (t->kind) { - case Temp::Formal: - case Temp::ScopedFormal: - case Temp::ScopedLocal: - return false; - case Temp::Local: - return !_variablesCanEscape; - case Temp::VirtualRegister: - return true; - default: - Q_UNREACHABLE(); - return false; - } - } - - void addUse(Temp *t) { - Q_ASSERT(t); - if (!isCollectible(t)) - return; - - _defUses[*t].uses.append(_stmt); - _usesPerStatement[_stmt].append(*t); - } - - void addDef(Temp *t) { - if (!isCollectible(t)) - return; - - Q_ASSERT(!_defUses.contains(*t) || _defUses.value(*t).defStmt == 0 || _defUses.value(*t).defStmt == _stmt); - - DefUse &defUse = _defUses[*t]; - defUse.defStmt = _stmt; - defUse.blockOfStatement = _block; - } - -public: - DefUsesCalculator(Function *function) - : _variablesCanEscape(function->variablesCanEscape()) - { - foreach (BasicBlock *bb, function->basicBlocks) { - _block = bb; - foreach (Stmt *stmt, bb->statements) { - _stmt = stmt; - stmt->accept(this); - } - } - - QMutableHashIterator it(_defUses); - while (it.hasNext()) { - it.next(); - if (!it.value().defStmt) - it.remove(); - } - } - - QList defs() const { - return _defUses.keys(); - } - - void removeDef(const Temp &var) { - _defUses.remove(var); - } - - void addUses(const Temp &variable, const QList &newUses) - { _defUses[variable].uses.append(newUses); } - - int useCount(const Temp &variable) const - { return _defUses[variable].uses.size(); } - - Stmt *defStmt(const Temp &variable) const - { return _defUses[variable].defStmt; } - - BasicBlock *defStmtBlock(const Temp &variable) const - { return _defUses[variable].blockOfStatement; } - - void removeUse(Stmt *usingStmt, const Temp &var) - { _defUses[var].uses.removeAll(usingStmt); } - - QList usedVars(Stmt *s) const - { return _usesPerStatement[s]; } - - QList uses(const Temp &var) const - { return _defUses[var].uses; } - - void dump() const - { - foreach (const Temp &var, _defUses.keys()) { - const DefUse &du = _defUses[var]; - var.dump(qout); - qout<<" -> defined in block "<index<<", statement: "; - du.defStmt->dump(qout); - qout<dump(qout);qout<expr->accept(this); } - virtual void visitJump(Jump *) {} - virtual void visitCJump(CJump *s) { s->cond->accept(this); } - virtual void visitRet(Ret *s) { s->expr->accept(this); } - virtual void visitTry(Try *) {} - - virtual void visitPhi(Phi *s) { - addDef(s->targetTemp); - foreach (Expr *e, s->incoming) - addUse(e->asTemp()); - } - - virtual void visitMove(Move *s) { - if (Temp *t = s->target->asTemp()) - addDef(t); - else - s->target->accept(this); - - s->source->accept(this); - } - - virtual void visitTemp(Temp *e) { addUse(e); } - - virtual void visitConst(Const *) {} - virtual void visitString(String *) {} - virtual void visitRegExp(RegExp *) {} - virtual void visitName(Name *) {} - virtual void visitClosure(Closure *) {} - virtual void visitConvert(Convert *e) { e->expr->accept(this); } - virtual void visitUnop(Unop *e) { e->expr->accept(this); } - virtual void visitBinop(Binop *e) { e->left->accept(this); e->right->accept(this); } - virtual void visitSubscript(Subscript *e) { e->base->accept(this); e->index->accept(this); } - virtual void visitMember(Member *e) { e->base->accept(this); } - virtual void visitCall(Call *e) { - e->base->accept(this); - for (ExprList *it = e->args; it; it = it->next) - it->expr->accept(this); - } - - virtual void visitNew(New *e) { - e->base->accept(this); - for (ExprList *it = e->args; it; it = it->next) - it->expr->accept(this); - } -}; - -bool hasPhiOnlyUses(Phi *phi, const DefUsesCalculator &defUses, QSet &collectedPhis) -{ - collectedPhis.insert(phi); - foreach (Stmt *use, defUses.uses(*phi->targetTemp)) { - if (Phi *dependentPhi = use->asPhi()) { - if (!collectedPhis.contains(dependentPhi)) { - if (!hasPhiOnlyUses(dependentPhi, defUses, collectedPhis)) - return false; - } - } else { - return false; - } - } - return true; -} - -void cleanupPhis(DefUsesCalculator &defUses) -{ - QLinkedList phis; - foreach (const Temp &def, defUses.defs()) - if (Phi *phi = defUses.defStmt(def)->asPhi()) - phis.append(phi); - - QSet toRemove; - while (!phis.isEmpty()) { - Phi *phi = phis.first(); - phis.removeFirst(); - if (toRemove.contains(phi)) - continue; - QSet collectedPhis; - if (hasPhiOnlyUses(phi, defUses, collectedPhis)) - toRemove.unite(collectedPhis); - } - - foreach (Phi *phi, toRemove) { - Temp targetVar = *phi->targetTemp; - - BasicBlock *bb = defUses.defStmtBlock(targetVar); - int idx = bb->statements.indexOf(phi); - bb->statements.remove(idx); - - foreach (const Temp &usedVar, defUses.usedVars(phi)) - defUses.removeUse(phi, usedVar); - defUses.removeDef(targetVar); - } -} - -class DeadCodeElimination: public ExprVisitor { - const bool variablesCanEscape; - DefUsesCalculator &_defUses; - QVector _worklist; - -public: - DeadCodeElimination(DefUsesCalculator &defUses, Function *function) - : variablesCanEscape(function->variablesCanEscape()) - , _defUses(defUses) - { - _worklist = QVector::fromList(_defUses.defs()); - } - - void run() { - while (!_worklist.isEmpty()) { - const Temp v = _worklist.first(); - _worklist.removeFirst(); - - if (_defUses.useCount(v) == 0) { -// qDebug()<<"-"< &stmts = _defUses.defStmtBlock(v)->statements; - int idx = stmts.indexOf(s); - if (idx != -1) - stmts.remove(idx); - foreach (const Temp &usedVar, _defUses.usedVars(s)) { - _defUses.removeUse(s, usedVar); - _worklist.append(usedVar); - } - _defUses.removeDef(v); - } - } - } - -#ifdef SHOW_SSA - qout<<"******************* After dead-code elimination:"; - _defUses.dump(); -#endif - } - -private: - bool _sideEffect; - - bool hasSideEffect(Stmt *s) { - // TODO: check if this can be moved to IR building. - _sideEffect = false; - if (Move *move = s->asMove()) { - if (Temp *t = move->target->asTemp()) { - switch (t->kind) { - case Temp::Formal: - case Temp::ScopedFormal: - case Temp::ScopedLocal: - return true; - case Temp::Local: - if (variablesCanEscape) - return true; - else - break; - case Temp::VirtualRegister: - break; - default: - Q_ASSERT(!"Invalid temp kind!"); - return true; - } - move->source->accept(this); - } else { - return true; - } - } - return _sideEffect; - } - -protected: - virtual void visitConst(Const *) {} - virtual void visitString(String *) {} - virtual void visitRegExp(RegExp *) {} - virtual void visitName(Name *e) { - // TODO: maybe we can distinguish between built-ins of which we know that they do not have - // a side-effect. - if (e->builtin == Name::builtin_invalid || (e->id && *e->id != QStringLiteral("this"))) - _sideEffect = true; - } - virtual void visitTemp(Temp *e) { - } - virtual void visitClosure(Closure *) {} - virtual void visitConvert(Convert *e) { - // we do not have type information yet, so: - _sideEffect = true; - } - - virtual void visitUnop(Unop *e) { - switch (e->op) { - case OpIncrement: - case OpDecrement: - _sideEffect = true; - break; - - default: - break; - } - - if (!_sideEffect) e->expr->accept(this); - } - virtual void visitBinop(Binop *e) { if (!_sideEffect) e->left->accept(this); if (!_sideEffect) e->right->accept(this); } - virtual void visitSubscript(Subscript *e) { - // TODO: see if we can have subscript accesses without side effect - _sideEffect = true; - } - virtual void visitMember(Member *e) { - // TODO: see if we can have member accesses without side effect - _sideEffect = true; - } - virtual void visitCall(Call *e) { - _sideEffect = true; // TODO: there are built-in functions that have no side effect. - } - virtual void visitNew(New *e) { - _sideEffect = true; // TODO: there are built-in types that have no side effect. - } -}; - -class TypeInference: public StmtVisitor, public ExprVisitor { - bool _variablesCanEscape; - const DefUsesCalculator &_defUses; - QHash _tempTypes; - QSet _worklist; - struct TypingResult { - int type; - bool fullyTyped; - - TypingResult(int type, bool fullyTyped): type(type), fullyTyped(fullyTyped) {} - explicit TypingResult(int type = UnknownType): type(type), fullyTyped(type != UnknownType) {} - }; - TypingResult _ty; - -public: - TypeInference(const DefUsesCalculator &defUses) - : _defUses(defUses) - , _ty(UnknownType) - {} - - void run(Function *function) { - _variablesCanEscape = function->variablesCanEscape(); - - // TODO: the worklist handling looks a bit inefficient... check if there is something better - _worklist.clear(); - for (int i = 0, ei = function->basicBlocks.size(); i != ei; ++i) { - BasicBlock *bb = function->basicBlocks[i]; - if (i == 0 || !bb->in.isEmpty()) - foreach (Stmt *s, bb->statements) - _worklist.insert(s); - } - - while (!_worklist.isEmpty()) { - QList worklist = _worklist.values(); - _worklist.clear(); - while (!worklist.isEmpty()) { - Stmt *s = worklist.first(); - worklist.removeFirst(); -#if defined(SHOW_SSA) - qout<<"Typing stmt ";s->dump(qout);qout<dump(qout);qout<dump(qout);qout<accept(this); - std::swap(_ty, ty); - return ty.fullyTyped; - } - - TypingResult run(Expr *e) { - TypingResult ty; - std::swap(_ty, ty); - e->accept(this); - std::swap(_ty, ty); - - if (ty.type != UnknownType) - setType(e, ty.type); - return ty; - } - - bool isAlwaysAnObject(Temp *t) { - switch (t->kind) { - case Temp::Formal: - case Temp::ScopedFormal: - case Temp::ScopedLocal: - return true; - case Temp::Local: - return _variablesCanEscape; - default: - return false; - } - } - - void setType(Expr *e, int ty) { - if (Temp *t = e->asTemp()) { -#if defined(SHOW_SSA) - qout<<"Setting type for "<< (t->scope?"scoped temp ":"temp ") <index<< " to "<type = ObjectType; - } else { - e->type = (Type) ty; - - if (_tempTypes[*t] != ty) { - _tempTypes[*t] = ty; - -#if defined(SHOW_SSA) - foreach (Stmt *s, _defUses.uses(*t)) { - qout << "Pushing back dependent stmt: "; - s->dump(qout); - qout << endl; - } -#endif - - _worklist += QSet::fromList(_defUses.uses(*t)); - } - } - } else { - e->type = (Type) ty; - } - } - -protected: - virtual void visitConst(Const *e) { _ty = TypingResult(e->type); } - virtual void visitString(String *) { _ty = TypingResult(StringType); } - virtual void visitRegExp(RegExp *) { _ty = TypingResult(ObjectType); } - virtual void visitName(Name *) { _ty = TypingResult(ObjectType); } - virtual void visitTemp(Temp *e) { - if (isAlwaysAnObject(e)) - _ty = TypingResult(ObjectType); - else - _ty = TypingResult(_tempTypes.value(*e, UnknownType)); - setType(e, _ty.type); - } - virtual void visitClosure(Closure *) { _ty = TypingResult(ObjectType); } // TODO: VERIFY THIS! - virtual void visitConvert(Convert *e) { - _ty = run(e->expr); - } - - virtual void visitUnop(Unop *e) { - _ty = run(e->expr); - switch (e->op) { - case OpUPlus: _ty.type = DoubleType; return; - case OpUMinus: _ty.type = DoubleType; return; - case OpCompl: _ty.type = SInt32Type; return; - case OpNot: _ty.type = BoolType; return; - - case OpIncrement: - case OpDecrement: - Q_ASSERT(!"Inplace operators should have been removed!"); - default: - Q_UNIMPLEMENTED(); - Q_UNREACHABLE(); - } - } - - virtual void visitBinop(Binop *e) { - TypingResult leftTy = run(e->left); - TypingResult rightTy = run(e->right); - _ty.fullyTyped = leftTy.fullyTyped && rightTy.fullyTyped; - - switch (e->op) { - case OpAdd: - if (leftTy.type & StringType || rightTy.type & StringType) - _ty.type = StringType; - else if (leftTy.type != UnknownType && rightTy.type != UnknownType) - _ty.type = DoubleType; - else - _ty.type = UnknownType; - break; - case OpSub: - _ty.type = DoubleType; - break; - - case OpMul: - case OpDiv: - case OpMod: - _ty.type = DoubleType; - break; - - case OpBitAnd: - case OpBitOr: - case OpBitXor: - case OpLShift: - case OpRShift: - _ty.type = SInt32Type; - break; - case OpURShift: - _ty.type = UInt32Type; - break; - - case OpGt: - case OpLt: - case OpGe: - case OpLe: - case OpEqual: - case OpNotEqual: - case OpStrictEqual: - case OpStrictNotEqual: - case OpAnd: - case OpOr: - case OpInstanceof: - case OpIn: - _ty.type = BoolType; - break; - - default: - Q_UNIMPLEMENTED(); - Q_UNREACHABLE(); - } - } - - virtual void visitCall(Call *e) { - _ty = run(e->base); - for (ExprList *it = e->args; it; it = it->next) - _ty.fullyTyped &= run(it->expr).fullyTyped; - _ty.type = ObjectType; - } - virtual void visitNew(New *e) { - _ty = run(e->base); - for (ExprList *it = e->args; it; it = it->next) - _ty.fullyTyped &= run(it->expr).fullyTyped; - _ty.type = ObjectType; - } - virtual void visitSubscript(Subscript *e) { - _ty.fullyTyped = run(e->base).fullyTyped && run(e->index).fullyTyped; - _ty.type = ObjectType; - } - - virtual void visitMember(Member *e) { - // TODO: for QML, try to do a static lookup - _ty = run(e->base); - _ty.type = ObjectType; - } - - virtual void visitExp(Exp *s) { _ty = run(s->expr); } - virtual void visitMove(Move *s) { - TypingResult sourceTy = run(s->source); - Q_ASSERT(s->op == OpInvalid); - if (Temp *t = s->target->asTemp()) { - setType(t, sourceTy.type); - _ty = sourceTy; - return; - } - - _ty = run(s->target); - _ty.fullyTyped &= sourceTy.fullyTyped; - } - - virtual void visitJump(Jump *) { _ty = TypingResult(MissingType); } - virtual void visitCJump(CJump *s) { _ty = run(s->cond); } - virtual void visitRet(Ret *s) { _ty = run(s->expr); } - virtual void visitTry(Try *s) { setType(s->exceptionVar, ObjectType); _ty = TypingResult(MissingType); } - virtual void visitPhi(Phi *s) { - _ty = run(s->incoming[0]); - for (int i = 1, ei = s->incoming.size(); i != ei; ++i) { - TypingResult ty = run(s->incoming[i]); - _ty.type |= ty.type; - _ty.fullyTyped &= ty.fullyTyped; - } - - // TODO: check & double check the next condition! - if (_ty.type & ObjectType || _ty.type & UndefinedType || _ty.type & NullType) - _ty.type = ObjectType; - else if (_ty.type & NumberType) - _ty.type = DoubleType; - - setType(s->targetTemp, _ty.type); - } -}; - -class TypePropagation: public StmtVisitor, public ExprVisitor { - Type _ty; - - void run(Expr *&e, Type requestedType = UnknownType) { - qSwap(_ty, requestedType); - e->accept(this); - qSwap(_ty, requestedType); - - if (requestedType != UnknownType) - if (e->type != requestedType) - if (requestedType & NumberType) { -// qDebug()<<"adding conversion from"<type)<<"to"< _conversions; - - void addConversion(Expr *&expr, Type targetType) { - _conversions.append(Conversion(&expr, targetType, _currStmt)); - } - -public: - TypePropagation() : _ty(UnknownType) {} - - void run(Function *f) { - foreach (BasicBlock *bb, f->basicBlocks) { - _conversions.clear(); - - foreach (Stmt *s, bb->statements) { - _currStmt = s; - s->accept(this); - } - - foreach (const Conversion &conversion, _conversions) { - if (conversion.stmt->asMove() && conversion.stmt->asMove()->source->asTemp()) { - *conversion.expr = bb->CONVERT(*conversion.expr, conversion.targetType); - } else { - Temp *target = bb->TEMP(bb->newTemp()); - target->type = conversion.targetType; - Expr *convert = bb->CONVERT(*conversion.expr, conversion.targetType); - Move *convCall = f->New(); - convCall->init(target, convert, OpInvalid); - - Temp *source = bb->TEMP(target->index); - source->type = conversion.targetType; - *conversion.expr = source; - - int idx = bb->statements.indexOf(conversion.stmt); - bb->statements.insert(idx, convCall); - } - } - } - } - -protected: - virtual void visitConst(Const *c) { - if (_ty & NumberType && c->type & NumberType) { - c->type = _ty; - } - } - - virtual void visitString(String *) {} - virtual void visitRegExp(RegExp *) {} - virtual void visitName(Name *) {} - virtual void visitTemp(Temp *) {} - virtual void visitClosure(Closure *) {} - virtual void visitConvert(Convert *e) { run(e->expr, e->type); } - virtual void visitUnop(Unop *e) { run(e->expr, e->type); } - virtual void visitBinop(Binop *e) { - // FIXME: This routine needs more tuning! - switch (e->op) { - case OpAdd: - case OpSub: - case OpMul: - case OpDiv: - case OpMod: - case OpBitAnd: - case OpBitOr: - case OpBitXor: - case OpLShift: - case OpRShift: - case OpURShift: - run(e->left, e->type); - run(e->right, e->type); - break; - - case OpGt: - case OpLt: - case OpGe: - case OpLe: - if (e->left->type == DoubleType) - run(e->right, DoubleType); - else if (e->right->type == DoubleType) - run(e->left, DoubleType); - else { - run(e->left, e->type); - run(e->right, e->type); - } - break; - - case OpEqual: - case OpNotEqual: - case OpStrictEqual: - case OpStrictNotEqual: - break; - - case OpInstanceof: - case OpIn: - run(e->left, e->type); - run(e->right, e->type); - break; - - default: - Q_UNIMPLEMENTED(); - Q_UNREACHABLE(); - } - } - virtual void visitCall(Call *e) { - run(e->base); - for (ExprList *it = e->args; it; it = it->next) - run(it->expr); - } - virtual void visitNew(New *e) { - run(e->base); - for (ExprList *it = e->args; it; it = it->next) - run(it->expr); - } - virtual void visitSubscript(Subscript *e) { run(e->base); run(e->index); } - virtual void visitMember(Member *e) { run(e->base); } - virtual void visitExp(Exp *s) { run(s->expr); } - virtual void visitMove(Move *s) { - run(s->target); - run(s->source, s->target->type); - } - virtual void visitJump(Jump *) {} - virtual void visitCJump(CJump *s) { - run(s->cond, BoolType); - } - virtual void visitRet(Ret *s) { run(s->expr); } - virtual void visitTry(Try *) {} - virtual void visitPhi(Phi *s) { - Type ty = s->targetTemp->type; - foreach (Expr *e, s->incoming) - if (e->asConst()) - run(e, ty); - } -}; - -void doEdgeSplitting(Function *f) -{ - const QVector oldBBs = f->basicBlocks; - - foreach (BasicBlock *bb, oldBBs) { - if (bb->in.size() > 1) { - for (int inIdx = 0, eInIdx = bb->in.size(); inIdx != eInIdx; ++inIdx) { - BasicBlock *inBB = bb->in[inIdx]; - if (inBB->out.size() > 1) { // this should have been split! -#if defined(SHOW_SSA) - qDebug() << "Splitting edge from block" << inBB->index << "to block" << bb->index; -#endif - - // create the basic block: - BasicBlock *newBB = new BasicBlock(f, bb->containingGroup()); - newBB->index = f->basicBlocks.last()->index + 1; - f->basicBlocks.append(newBB); - Jump *s = f->New(); - s->init(bb); - newBB->statements.append(s); - - // rewire the old outgoing edge - int outIdx = inBB->out.indexOf(bb); - inBB->out[outIdx] = newBB; - newBB->in.append(inBB); - - // rewire the old incoming edge - bb->in[inIdx] = newBB; - newBB->out.append(bb); - - // patch the terminator - Stmt *terminator = inBB->terminator(); - if (Jump *j = terminator->asJump()) { - Q_ASSERT(outIdx == 0); - j->target = newBB; - } else if (CJump *j = terminator->asCJump()) { - if (outIdx == 0) - j->iftrue = newBB; - else if (outIdx == 1) - j->iffalse = newBB; - else - Q_ASSERT(!"Invalid out edge index for CJUMP!"); - } else { - Q_ASSERT(!"Unknown terminator!"); - } - } - } - } - } -} - -QHash scheduleBlocks(Function *function, const DominatorTree &df) -{ - struct I { - const DominatorTree &df; - QHash &startEndLoops; - QSet visited; - QVector &sequence; - BasicBlock *currentGroup; - QList postponed; - - I(const DominatorTree &df, QVector &sequence, - QHash &startEndLoops) - : df(df) - , sequence(sequence) - , startEndLoops(startEndLoops) - , currentGroup(0) - {} - - void DFS(BasicBlock *bb) { - Q_ASSERT(bb); - if (visited.contains(bb)) - return; - - if (bb->containingGroup() != currentGroup) { - postponed.append(bb); - return; - } - if (bb->isGroupStart()) - currentGroup = bb; - else if (bb->in.size() > 1) - foreach (BasicBlock *inBB, bb->in) - if (!visited.contains(inBB)) - return; - - Q_ASSERT(df.immediateDominator(bb) == 0 || sequence.contains(df.immediateDominator(bb))); - layout(bb); - if (Stmt *terminator = bb->terminator()) { - if (Jump *j = terminator->asJump()) { - Q_ASSERT(bb->out.size() == 1); - DFS(j->target); - } else if (CJump *cj = terminator->asCJump()) { - Q_ASSERT(bb->out.size() == 2); - DFS(cj->iftrue); - DFS(cj->iffalse); - } else if (terminator->asRet()) { - Q_ASSERT(bb->out.size() == 0); - // nothing to do. - } else { - Q_UNREACHABLE(); - } - } else { - Q_UNREACHABLE(); - } - - if (bb->isGroupStart()) { - currentGroup = bb->containingGroup(); - startEndLoops.insert(bb, sequence.last()); - QList p = postponed; - foreach (BasicBlock *pBB, p) - DFS(pBB); - } - } - - void layout(BasicBlock *bb) { - sequence.append(bb); - visited.insert(bb); - postponed.removeAll(bb); - } - }; - - QVector sequence; - sequence.reserve(function->basicBlocks.size()); - QHash startEndLoops; - I(df, sequence, startEndLoops).DFS(function->basicBlocks.first()); - qSwap(function->basicBlocks, sequence); - - showMeTheCode(function); - return startEndLoops; -} - -void checkCriticalEdges(QVector basicBlocks) { - foreach (BasicBlock *bb, basicBlocks) { - if (bb && bb->out.size() > 1) { - foreach (BasicBlock *bb2, bb->out) { - if (bb2 && bb2->in.size() > 1) { - qout << "found critical edge between block " - << bb->index << " and block " << bb2->index; - Q_ASSERT(false); - } - } - } - } -} - -void cleanupBasicBlocks(Function *function) -{ -// showMeTheCode(function); - - // remove all basic blocks that have no incoming edges, but skip the entry block - QVector W = function->basicBlocks; - W.removeFirst(); - QSet toRemove; - - while (!W.isEmpty()) { - BasicBlock *bb = W.first(); - W.removeFirst(); - if (toRemove.contains(bb)) - continue; - if (bb->in.isEmpty()) { - foreach (BasicBlock *outBB, bb->out) { - int idx = outBB->in.indexOf(bb); - if (idx != -1) { - outBB->in.remove(idx); - W.append(outBB); - } - } - toRemove.insert(bb); - } - } - - // TODO: merge 2 basic blocks A and B if A has one outgoing edge (to B), B has one incoming - // edge (from A), but not when A has more than 1 incoming edge and B has more than one - // outgoing edge. - - foreach (BasicBlock *bb, toRemove) { - foreach (Stmt *s, bb->statements) - s->destroyData(); - int idx = function->basicBlocks.indexOf(bb); - if (idx != -1) - function->basicBlocks.remove(idx); - delete bb; - } - - // re-number all basic blocks: - for (int i = 0; i < function->basicBlocks.size(); ++i) - function->basicBlocks[i]->index = i; -} - -class InputOutputCollector: protected StmtVisitor, protected ExprVisitor { - const bool variablesCanEscape; - -public: - QList inputs; - QList outputs; - - InputOutputCollector(bool variablesCanEscape): variablesCanEscape(variablesCanEscape) {} - - void collect(Stmt *s) { - inputs.clear(); - outputs.clear(); - s->accept(this); - } - -protected: - virtual void visitConst(Const *) {} - virtual void visitString(String *) {} - virtual void visitRegExp(RegExp *) {} - virtual void visitName(Name *) {} - virtual void visitTemp(Temp *e) { - switch (e->kind) { - case Temp::Local: - if (!variablesCanEscape) - inputs.append(*e); - break; - - case Temp::VirtualRegister: - inputs.append(*e); - break; - - default: - break; - } - } - virtual void visitClosure(Closure *) {} - virtual void visitConvert(Convert *e) { e->expr->accept(this); } - virtual void visitUnop(Unop *e) { e->expr->accept(this); } - virtual void visitBinop(Binop *e) { e->left->accept(this); e->right->accept(this); } - virtual void visitCall(Call *e) { - e->base->accept(this); - for (ExprList *it = e->args; it; it = it->next) - it->expr->accept(this); - } - virtual void visitNew(New *e) { - e->base->accept(this); - for (ExprList *it = e->args; it; it = it->next) - it->expr->accept(this); - } - virtual void visitSubscript(Subscript *e) { e->base->accept(this); e->index->accept(this); } - virtual void visitMember(Member *e) { e->base->accept(this); } - virtual void visitExp(Exp *s) { s->expr->accept(this); } - virtual void visitMove(Move *s) { - s->source->accept(this); - if (Temp *t = s->target->asTemp()) { - if ((t->kind == Temp::Local && !variablesCanEscape) || t->kind == Temp::VirtualRegister) - outputs.append(*t); - else - s->target->accept(this); - } else { - s->target->accept(this); - } - } - virtual void visitJump(Jump *) {} - virtual void visitCJump(CJump *s) { s->cond->accept(this); } - virtual void visitRet(Ret *s) { s->expr->accept(this); } - virtual void visitTry(Try *) {} - virtual void visitPhi(Phi *s) { - // Handled separately - } -}; - -/* - * The algorithm is described in: - * - * Linear Scan Register Allocation on SSA Form - * Christian Wimmer & Michael Franz, CGO'10, April 24-28, 2010 - * - * There is one slight difference w.r.t. the phi-nodes: in the artice, the phi nodes are attached - * to the basic-blocks. Therefore, in the algorithm in the article, the ranges for input parameters - * for phi nodes run from their definition upto the branch instruction into the block with the phi - * node. In our representation, phi nodes are mostly treaded as normal instructions, so we have to - * enlarge the range to cover the phi node itself. - */ -class LifeRanges { - typedef QSet LiveRegs; - - QHash _liveIn; - QHash _intervals; - QList _sortedRanges; - -public: - LifeRanges(Function *function, const QHash &startEndLoops) - { - int id = 0; - foreach (BasicBlock *bb, function->basicBlocks) { - foreach (Stmt *s, bb->statements) { - if (s->asPhi()) - s->id = id + 1; - else - s->id = ++id; - } - } - - for (int i = function->basicBlocks.size() - 1; i >= 0; --i) { - BasicBlock *bb = function->basicBlocks[i]; - buildIntervals(bb, startEndLoops.value(bb, 0), function->variablesCanEscape()); - } - - _sortedRanges.reserve(_intervals.size()); - for (QHash::const_iterator i = _intervals.begin(), ei = _intervals.end(); i != ei; ++i) { - LifeTimeInterval range = i.value(); - range.setTemp(i.key()); - _sortedRanges.append(range); - } - qSort(_sortedRanges.begin(), _sortedRanges.end(), LifeTimeInterval::lessThan); - } - - QList ranges() const { return _sortedRanges; } - - void dump() const - { - qout << "Life ranges:" << endl; - qout << "Intervals:" << endl; - foreach (const LifeTimeInterval &range, _sortedRanges) { - range.dump(); - qout << endl; - } - - foreach (BasicBlock *bb, _liveIn.keys()) { - qout << "L" << bb->index <<" live-in: "; - QList live = QList::fromSet(_liveIn.value(bb)); - qSort(live); - for (int i = 0; i < live.size(); ++i) { - if (i > 0) qout << ", "; - live[i].dump(qout); - } - qout << endl; - } - } - -private: - void buildIntervals(BasicBlock *bb, BasicBlock *loopEnd, bool variablesCanEscape) - { - LiveRegs live; - foreach (BasicBlock *successor, bb->out) { - live.unite(_liveIn[successor]); - const int bbIndex = successor->in.indexOf(bb); - Q_ASSERT(bbIndex >= 0); - - foreach (Stmt *s, successor->statements) { - if (Phi *phi = s->asPhi()) { - if (Temp *t = phi->incoming[bbIndex]->asTemp()) - live.insert(*t); - } else { - break; - } - } - } - - foreach (const Temp &opd, live) - _intervals[opd].addRange(bb->statements.first(), bb->statements.last()); - - InputOutputCollector collector(variablesCanEscape); - for (int i = bb->statements.size() - 1; i >= 0; --i) { - Stmt *s = bb->statements[i]; - if (Phi *phi = s->asPhi()) { - live.remove(*phi->targetTemp); - continue; - } - collector.collect(s); - foreach (const Temp &opd, collector.outputs) { - _intervals[opd].setFrom(s); - live.remove(opd); - } - foreach (const Temp &opd, collector.inputs) { - _intervals[opd].addRange(bb->statements.first(), s); - live.insert(opd); - } - } - - if (loopEnd) { // Meaning: bb is a loop header, because loopEnd is set to non-null. - foreach (const Temp &opd, live) - _intervals[opd].addRange(bb->statements.first(), loopEnd->statements.last()); - } - - _liveIn[bb] = live; - } -}; -} // anonymous namespace - -void LifeTimeInterval::setFrom(Stmt *from) { - Q_ASSERT(from && from->id > 0); - - if (_ranges.isEmpty()) // this is the case where there is no use, only a define - _ranges.push_front(Range(from->id, from->id)); - else - _ranges.first().start = from->id; -} - -void LifeTimeInterval::addRange(Stmt *from, Stmt *to) { - Q_ASSERT(from && from->id > 0); - Q_ASSERT(to && to->id > 0); - Q_ASSERT(to->id >= from->id); - - if (_ranges.isEmpty()) { - _ranges.push_front(Range(from->id, to->id)); - return; - } - - Range *p = &_ranges.first(); - if (to->id + 1 >= p->start && p->end + 1 >= from->id) { - p->start = qMin(p->start, from->id); - p->end = qMax(p->end, to->id); - while (_ranges.count() > 1) { - Range *p1 = &_ranges[1]; - if (p->end + 1 < p1->start || p1->end + 1 < p->start) - break; - p1->start = qMin(p->start, p1->start); - p1->end = qMax(p->end, p1->end); - _ranges.pop_front(); - p = &_ranges.first(); - } - } else { - Q_ASSERT(to->id < p->start); - _ranges.push_front(Range(from->id, to->id)); - } -} - -void LifeTimeInterval::dump() const { - _temp.dump(qout); - qout << ": "; - if (_ranges.isEmpty()) - qout << "(none)"; - for (int i = 0; i < _ranges.size(); ++i) { - if (i > 0) qout << ", "; - qout << _ranges[i].start << " - " << _ranges[i].end; - } - if (_reg != Invalid) - qout << " (register " << _reg << ")"; -} - -bool LifeTimeInterval::lessThan(const LifeTimeInterval &r1, const LifeTimeInterval &r2) { - if (r1._ranges.first().start == r2._ranges.first().start) - return r1._ranges.last().end < r2._ranges.last().end; - else - return r1._ranges.first().start < r2._ranges.first().start; -} - -void Optimizer::run() -{ -#if defined(SHOW_SSA) - qout << "##### NOW IN FUNCTION " << (_function->name ? qPrintable(*_function->name) : "anonymous!") - << " with " << _function->basicBlocks.size() << " basic blocks." << endl << flush; -#endif - - // Number all basic blocks, so we have nice numbers in the dumps: - for (int i = 0; i < function->basicBlocks.size(); ++i) - function->basicBlocks[i]->index = i; - showMeTheCode(function); - - cleanupBasicBlocks(function); - - function->removeSharedExpressions(); - -// showMeTheCode(function); - - if (!function->hasTry && !function->hasWith) { -// qout << "Starting edge splitting..." << endl; - doEdgeSplitting(function); -// showMeTheCode(function); - - // Calculate the dominator tree: - DominatorTree df(function->basicBlocks); - - convertToSSA(function, df); -// showMeTheCode(function); - -// qout << "Starting def/uses calculation..." << endl; - DefUsesCalculator defUses(function); - -// qout << "Cleaning up phi nodes..." << endl; - cleanupPhis(defUses); -// showMeTheCode(function); - -// qout << "Starting dead-code elimination..." << endl; - DeadCodeElimination(defUses, function).run(); -// showMeTheCode(function); - -// qout << "Running type inference..." << endl; - TypeInference(defUses).run(function); -// showMeTheCode(function); - -// qout << "Doing type propagation..." << endl; - TypePropagation().run(function); -// showMeTheCode(function); - -// qout << "Doing block scheduling..." << endl; - startEndLoops = scheduleBlocks(function, df); -// showMeTheCode(function); - -#ifndef QT_NO_DEBUG - checkCriticalEdges(function->basicBlocks); -#endif - -// qout << "Finished." << endl; - inSSA = true; - } else { - inSSA = false; - } -} - -namespace { -void insertMove(Function *function, BasicBlock *basicBlock, Temp *target, Expr *source) { - if (target->type != source->type) - source = basicBlock->CONVERT(source, target->type); - - Move *s = function->New(); - s->init(target, source, OpInvalid); - basicBlock->statements.insert(basicBlock->statements.size() - 1, s); -} -} - -/* - * Quick function to convert out of SSA, so we can put the stuff through the ISel phases. This - * has to be replaced by a phase in the specific ISel back-ends and do register allocation at the - * same time. That way the huge number of redundant moves generated by this function are eliminated. - */ -void Optimizer::convertOutOfSSA() { - // We assume that edge-splitting is already done. - foreach (BasicBlock *bb, function->basicBlocks) { - QVector &stmts = bb->statements; - while (!stmts.isEmpty()) { - Stmt *s = stmts.first(); - if (Phi *phi = s->asPhi()) { - stmts.removeFirst(); - for (int i = 0, ei = phi->incoming.size(); i != ei; ++i) - insertMove(function, bb->in[i], phi->targetTemp, phi->incoming[i]); - } else { - break; - } - } - } -} - -QList Optimizer::ssaDeconstructionMoves(BasicBlock *basicBlock) -{ - QList moves; - - foreach (BasicBlock *outEdge, basicBlock->out) { - int inIdx = outEdge->in.indexOf(basicBlock); - Q_ASSERT(inIdx >= 0); - foreach (Stmt *s, outEdge->statements) { - if (Phi *phi = s->asPhi()) { - SSADeconstructionMove m; - m.source = phi->incoming[inIdx]; - m.target = phi->targetTemp; - moves.append(m); - } else { - break; - } - } - } - - return moves; -} - -QList Optimizer::lifeRanges() const -{ - Q_ASSERT(isInSSA()); - - LifeRanges lifeRanges(function, startEndLoops); -// lifeRanges.dump(); -// showMeTheCode(function); - return lifeRanges.ranges(); -} diff --git a/src/qml/qml/v4/qv4ssa_p.h b/src/qml/qml/v4/qv4ssa_p.h deleted file mode 100644 index 097a40eff1..0000000000 --- a/src/qml/qml/v4/qv4ssa_p.h +++ /dev/null @@ -1,127 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QV4SSA_P_H -#define QV4SSA_P_H - -#include "qv4jsir_p.h" - -QT_BEGIN_NAMESPACE -namespace QQmlJS { -namespace V4IR { - -class LifeTimeInterval { - struct Range { - int start; - int end; - - Range(int start = Invalid, int end = Invalid) - : start(start) - , end(end) - {} - }; - - Temp _temp; - QList _ranges; - int _reg; - -public: - static const int Invalid = -1; - - LifeTimeInterval() - : _reg(Invalid) - {} - - void setTemp(const Temp &temp) { this->_temp = temp; } - Temp temp() const { return _temp; } - - void setFrom(Stmt *from); - void addRange(Stmt *from, Stmt *to); - - int start() const { return _ranges.first().start; } - int end() const { return _ranges.last().end; } - - int reg() const { return _reg; } - void setReg(int reg) { _reg = reg; } - - void dump() const; - static bool lessThan(const LifeTimeInterval &r1, const LifeTimeInterval &r2); -}; - -class Optimizer -{ -public: - struct SSADeconstructionMove - { - Expr *source; - Temp *target; - - bool needsConversion() const - { return target->type != source->type; } - }; - -public: - Optimizer(Function *function) - : function(function) - , inSSA(false) - {} - - void run(); - void convertOutOfSSA(); - - bool isInSSA() const - { return inSSA; } - - QList ssaDeconstructionMoves(BasicBlock *basicBlock); - - QList lifeRanges() const; - -private: - Function *function; - bool inSSA; - QHash startEndLoops; -}; - -} // V4IR namespace -} // QQmlJS namespace -QT_END_NAMESPACE - -#endif // QV4SSA_P_H diff --git a/src/qml/qml/v4/qv4stacktrace.cpp b/src/qml/qml/v4/qv4stacktrace.cpp deleted file mode 100644 index 1cc2e53556..0000000000 --- a/src/qml/qml/v4/qv4stacktrace.cpp +++ /dev/null @@ -1,146 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#if defined(_WIN32) -#include -#include -#endif - -#include "qv4stacktrace_p.h" -#include "qv4function_p.h" -#include "qv4engine_p.h" -#include "qv4unwindhelper_p.h" - -#if (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)) || defined(Q_OS_MAC) -#define HAVE_GNU_BACKTRACE -#include -#endif - -QT_BEGIN_NAMESPACE - -using namespace QV4; - -NativeStackTrace::NativeStackTrace(ExecutionContext *context) -{ - engine = context->engine; - currentNativeFrame = 0; - -#if defined(HAVE_GNU_BACKTRACE) - UnwindHelper::prepareForUnwind(context); - - nativeFrameCount = backtrace(&trace[0], sizeof(trace) / sizeof(trace[0])); -#elif defined(Q_OS_WIN) - - int machineType = 0; - - CONTEXT winContext; - memset(&winContext, 0, sizeof(winContext)); - winContext.ContextFlags = CONTEXT_FULL; - RtlCaptureContext(&winContext); - - STACKFRAME64 sf64; - memset(&sf64, 0, sizeof(sf64)); - -#if defined(Q_PROCESSOR_X86_32) - machineType = IMAGE_FILE_MACHINE_I386; - - sf64.AddrFrame.Offset = winContext.Ebp; - sf64.AddrFrame.Mode = AddrModeFlat; - sf64.AddrPC.Offset = winContext.Eip; - sf64.AddrPC.Mode = AddrModeFlat; - sf64.AddrStack.Offset = winContext.Esp; - sf64.AddrStack.Mode = AddrModeFlat; - -#elif defined(Q_PROCESSOR_X86_64) - machineType = IMAGE_FILE_MACHINE_AMD64; - - sf64.AddrFrame.Offset = winContext.Rbp; - sf64.AddrFrame.Mode = AddrModeFlat; - sf64.AddrPC.Offset = winContext.Rip; - sf64.AddrPC.Mode = AddrModeFlat; - sf64.AddrStack.Offset = winContext.Rsp; - sf64.AddrStack.Mode = AddrModeFlat; - -#else -#error "Platform unsupported!" -#endif - - nativeFrameCount = 0; - - while (StackWalk64(machineType, GetCurrentProcess(), GetCurrentThread(), &sf64, &winContext, 0, SymFunctionTableAccess64, SymGetModuleBase64, 0)) { - - if (sf64.AddrReturn.Offset == 0) - break; - - trace[nativeFrameCount] = reinterpret_cast(sf64.AddrReturn.Offset); - nativeFrameCount++; - if (nativeFrameCount >= sizeof(trace) / sizeof(trace[0])) - break; - } - -#else - nativeFrameCount = 0; -#endif - } - -NativeFrame NativeStackTrace::nextFrame() { - NativeFrame frame; - frame.function = 0; - frame.line = -1; - - for (; currentNativeFrame < nativeFrameCount && !frame.function; ++currentNativeFrame) { - quintptr pc = reinterpret_cast(trace[currentNativeFrame]); - // The pointers from the back trace point to the return address, but we are interested in - // the caller site. - pc = pc - 1; - - Function *f = engine->functionForProgramCounter(pc); - if (!f) - continue; - - frame.function = f; - frame.line = f->lineNumberForProgramCounter(pc - reinterpret_cast(f->code)); - } - - return frame; -} - -QT_END_NAMESPACE diff --git a/src/qml/qml/v4/qv4stacktrace_p.h b/src/qml/qml/v4/qv4stacktrace_p.h deleted file mode 100644 index 79cb4d1813..0000000000 --- a/src/qml/qml/v4/qv4stacktrace_p.h +++ /dev/null @@ -1,75 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QV4STACKTRACE_P_H -#define QV4STACKTRACE_P_H - -#include - -QT_BEGIN_NAMESPACE - -namespace QV4 { - -struct Function; -struct ExecutionEngine; -struct ExecutionContext; - -struct NativeFrame { - Function *function; - int line; -}; - -struct NativeStackTrace -{ - void *trace[100]; - int nativeFrameCount; - int currentNativeFrame; - ExecutionEngine *engine; - - NativeStackTrace(ExecutionContext *context); - - NativeFrame nextFrame(); -}; - -} // namespace QV4 - -QT_END_NAMESPACE - -#endif // QV4STACKTRACE_P_H diff --git a/src/qml/qml/v4/qv4string.cpp b/src/qml/qml/v4/qv4string.cpp deleted file mode 100644 index 8b78c40129..0000000000 --- a/src/qml/qml/v4/qv4string.cpp +++ /dev/null @@ -1,321 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4string_p.h" -#include "qv4identifiertable_p.h" -#include "qv4runtime_p.h" -#include "qv4objectproto_p.h" -#include "qv4stringobject_p.h" -#include - -using namespace QV4; - -static uint toArrayIndex(const QChar *ch, const QChar *end, bool *ok) -{ - *ok = false; - uint i = ch->unicode() - '0'; - if (i > 9) - return UINT_MAX; - ++ch; - // reject "01", "001", ... - if (i == 0 && ch != end) - return UINT_MAX; - - while (ch < end) { - uint x = ch->unicode() - '0'; - if (x > 9) - return UINT_MAX; - uint n = i*10 + x; - if (n < i) - // overflow - return UINT_MAX; - i = n; - ++ch; - } - *ok = true; - return i; -} - -static uint toArrayIndex(const char *ch, const char *end, bool *ok) -{ - *ok = false; - uint i = *ch - '0'; - if (i > 9) - return UINT_MAX; - ++ch; - // reject "01", "001", ... - if (i == 0 && ch != end) - return UINT_MAX; - - while (ch < end) { - uint x = *ch - '0'; - if (x > 9) - return UINT_MAX; - uint n = i*10 + x; - if (n < i) - // overflow - return UINT_MAX; - i = n; - ++ch; - } - *ok = true; - return i; -} - - -const ManagedVTable String::static_vtbl = -{ - call, - construct, - 0 /*markObjects*/, - destroy, - 0 /*collectDeletables*/, - hasInstance, - get, - getIndexed, - put, - putIndexed, - query, - queryIndexed, - deleteProperty, - deleteIndexedProperty, - 0 /*getLookup*/, - 0 /*setLookup*/, - isEqualTo, - 0 /*advanceIterator*/, - "String", -}; - -void String::destroy(Managed *that) -{ - static_cast(that)->~String(); -} - -Value String::get(Managed *m, String *name, bool *hasProperty) -{ - String *that = static_cast(m); - ExecutionEngine *v4 = m->engine(); - if (name == v4->id_length) { - if (hasProperty) - *hasProperty = true; - return Value::fromInt32(that->_text.length()); - } - PropertyAttributes attrs; - Property *pd = v4->stringPrototype->__getPropertyDescriptor__(name, &attrs); - if (!pd || attrs.isGeneric()) { - if (hasProperty) - *hasProperty = false; - return Value::undefinedValue(); - } - if (hasProperty) - *hasProperty = true; - return v4->stringPrototype->getValue(Value::fromString(that), pd, attrs); -} - -Value String::getIndexed(Managed *m, uint index, bool *hasProperty) -{ - String *that = static_cast(m); - ExecutionEngine *engine = that->engine(); - if (index < that->_text.length()) { - if (hasProperty) - *hasProperty = true; - return Value::fromString(engine->newString(that->toQString().mid(index, 1))); - } - PropertyAttributes attrs; - Property *pd = engine->stringPrototype->__getPropertyDescriptor__(index, &attrs); - if (!pd || attrs.isGeneric()) { - if (hasProperty) - *hasProperty = false; - return Value::undefinedValue(); - } - if (hasProperty) - *hasProperty = true; - return engine->stringPrototype->getValue(Value::fromString(that), pd, attrs); -} - -void String::put(Managed *m, String *name, const Value &value) -{ - String *that = static_cast(m); - Object *o = that->engine()->newStringObject(Value::fromString(that)); - o->put(name, value); -} - -void String::putIndexed(Managed *m, uint index, const Value &value) -{ - String *that = static_cast(m); - Object *o = m->engine()->newStringObject(Value::fromString(that)); - o->putIndexed(index, value); -} - -PropertyAttributes String::query(const Managed *m, String *name) -{ - uint idx = name->asArrayIndex(); - if (idx != UINT_MAX) - return queryIndexed(m, idx); - return Attr_Invalid; -} - -PropertyAttributes String::queryIndexed(const Managed *m, uint index) -{ - const String *that = static_cast(m); - return (index < that->_text.length()) ? Attr_NotConfigurable|Attr_NotWritable : Attr_Invalid; -} - -bool String::deleteProperty(Managed *, String *) -{ - return false; -} - -bool String::deleteIndexedProperty(Managed *m, uint index) -{ - return false; -} - -bool String::isEqualTo(Managed *t, Managed *o) -{ - if (t == o) - return true; - String *that = static_cast(t); - String *other = static_cast(o); - if (that->hashValue() != other->hashValue()) - return false; - if (that->identifier && that->identifier == other->identifier) - return true; - if (that->subtype >= StringType_UInt && that->subtype == other->subtype) - return true; - - return that->toQString() == other->toQString(); -} - - -String::String(ExecutionEngine *engine, const QString &text) - : Managed(engine ? engine->emptyClass : 0), _text(text), identifier(0), stringHash(UINT_MAX) -{ - vtbl = &static_vtbl; - type = Type_String; - subtype = StringType_Unknown; -} - -uint String::toUInt(bool *ok) const -{ - *ok = true; - - if (subtype == StringType_Unknown) - createHashValue(); - if (subtype >= StringType_UInt) - return stringHash; - - // ### this conversion shouldn't be required - double d = __qmljs_string_to_number(toQString()); - uint l = (uint)d; - if (d == l) - return l; - *ok = false; - return UINT_MAX; -} - -void String::makeIdentifierImpl() -{ - engine()->identifierTable->identifier(this); -} - -void String::createHashValue() const -{ - const QChar *ch = _text.constData(); - const QChar *end = ch + _text.length(); - - // array indices get their number as hash value - bool ok; - stringHash = toArrayIndex(ch, end, &ok); - if (ok) { - subtype = (stringHash == UINT_MAX) ? StringType_UInt : StringType_ArrayIndex; - return; - } - - uint h = 0xffffffff; - while (ch < end) { - h = 31 * h + ch->unicode(); - ++ch; - } - - stringHash = h; - subtype = StringType_Regular; -} - -uint String::createHashValue(const QChar *ch, int length) -{ - const QChar *end = ch + length; - - // array indices get their number as hash value - bool ok; - uint stringHash = toArrayIndex(ch, end, &ok); - if (ok) - return stringHash; - - uint h = 0xffffffff; - while (ch < end) { - h = 31 * h + ch->unicode(); - ++ch; - } - - return h; -} - -uint String::createHashValue(const char *ch, int length) -{ - const char *end = ch + length; - - // array indices get their number as hash value - bool ok; - uint stringHash = toArrayIndex(ch, end, &ok); - if (ok) - return stringHash; - - uint h = 0xffffffff; - while (ch < end) { - if (*ch >= 0x80) - return UINT_MAX; - h = 31 * h + *ch; - ++ch; - } - - return h; -} diff --git a/src/qml/qml/v4/qv4string_p.h b/src/qml/qml/v4/qv4string_p.h deleted file mode 100644 index 31e5c2a5f7..0000000000 --- a/src/qml/qml/v4/qv4string_p.h +++ /dev/null @@ -1,146 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QV4STRING_H -#define QV4STRING_H - -#include -#include "qv4managed_p.h" - -QT_BEGIN_NAMESPACE - -namespace QV4 { - -struct ExecutionEngine; -struct Identifier; - -struct Q_QML_EXPORT String : public Managed { - enum StringType { - StringType_Unknown, - StringType_Regular, - StringType_UInt, - StringType_ArrayIndex - }; - - String() : Managed(0), identifier(0), stringHash(UINT_MAX) - { vtbl = &static_vtbl; type = Type_String; subtype = StringType_Unknown; } - String(ExecutionEngine *engine, const QString &text); - ~String() { _data = 0; } - - inline bool isEqualTo(const String *other) const { - if (this == other) - return true; - if (hashValue() != other->hashValue()) - return false; - if (identifier && identifier == other->identifier) - return true; - if (subtype >= StringType_UInt && subtype == other->subtype) - return true; - - return toQString() == other->toQString(); - } - inline bool compare(const String *other) { - return toQString() < other->toQString(); - } - - inline bool isEmpty() const { return _text.isEmpty(); } - inline const QString &toQString() const { - return _text; - } - - inline unsigned hashValue() const { - if (subtype == StringType_Unknown) - createHashValue(); - - return stringHash; - } - uint asArrayIndex() const { - if (subtype == StringType_Unknown) - createHashValue(); - if (subtype == StringType_ArrayIndex) - return stringHash; - return UINT_MAX; - } - uint toUInt(bool *ok) const; - - void makeIdentifier() { - if (identifier) - return; - makeIdentifierImpl(); - } - - void makeIdentifierImpl(); - - void createHashValue() const; - static uint createHashValue(const QChar *ch, int length); - static uint createHashValue(const char *ch, int length); - - bool startsWithUpper() const { - return _text.length() && _text.at(0).isUpper(); - } - int length() const { - return _text.length(); - } - - QString _text; - mutable Identifier *identifier; - mutable uint stringHash; - - -protected: - static void destroy(Managed *); - static Value get(Managed *m, String *name, bool *hasProperty); - static Value getIndexed(Managed *m, uint index, bool *hasProperty); - static void put(Managed *m, String *name, const Value &value); - static void putIndexed(Managed *m, uint index, const Value &value); - static PropertyAttributes query(const Managed *m, String *name); - static PropertyAttributes queryIndexed(const Managed *m, uint index); - static bool deleteProperty(Managed *, String *); - static bool deleteIndexedProperty(Managed *m, uint index); - static bool isEqualTo(Managed *that, Managed *o); - - static const ManagedVTable static_vtbl; -}; - -} - -QT_END_NAMESPACE - -#endif diff --git a/src/qml/qml/v4/qv4stringobject.cpp b/src/qml/qml/v4/qv4stringobject.cpp deleted file mode 100644 index 5afedd3d4f..0000000000 --- a/src/qml/qml/v4/qv4stringobject.cpp +++ /dev/null @@ -1,761 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - - -#include "qv4stringobject_p.h" -#include "qv4regexpobject_p.h" -#include "qv4objectproto_p.h" -#include "qv4mm_p.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#ifndef Q_OS_WIN -# include -# ifndef Q_OS_VXWORKS -# include -# else -# include "qplatformdefs.h" -# endif -#else -# include -#endif - -using namespace QV4; - -DEFINE_MANAGED_VTABLE(StringObject); - -StringObject::StringObject(ExecutionEngine *engine, const Value &value) - : Object(engine), value(value) -{ - vtbl = &static_vtbl; - type = Type_StringObject; - - tmpProperty.value = Value::undefinedValue(); - - assert(value.isString()); - defineReadonlyProperty(engine->id_length, Value::fromUInt32(value.stringValue()->toQString().length())); -} - -Property *StringObject::getIndex(uint index) const -{ - QString str = value.stringValue()->toQString(); - if (index >= (uint)str.length()) - return 0; - String *result = internalClass->engine->newString(str.mid(index, 1)); - tmpProperty.value = Value::fromString(result); - return &tmpProperty; -} - -bool StringObject::deleteIndexedProperty(Managed *m, uint index) -{ - StringObject *o = m->asStringObject(); - if (!o) - m->engine()->current->throwTypeError(); - - if (index < o->value.stringValue()->toQString().length()) { - if (m->engine()->current->strictMode) - m->engine()->current->throwTypeError(); - return false; - } - return true; -} - -Property *StringObject::advanceIterator(Managed *m, ObjectIterator *it, String **name, uint *index, PropertyAttributes *attrs) -{ - StringObject *s = static_cast(m); - uint slen = s->value.stringValue()->toQString().length(); - if (it->arrayIndex < slen) { - while (it->arrayIndex < slen) { - *index = it->arrayIndex; - ++it->arrayIndex; - if (attrs) - *attrs = s->arrayAttributes ? s->arrayAttributes[it->arrayIndex] : PropertyAttributes(Attr_NotWritable|Attr_NotConfigurable); - return s->__getOwnProperty__(*index); - } - it->arrayNode = s->sparseArrayBegin(); - // iterate until we're past the end of the string - while (it->arrayNode && it->arrayNode->key() < slen) - it->arrayNode = it->arrayNode->nextNode(); - } - - return Object::advanceIterator(m, it, name, index, attrs); -} - -void StringObject::markObjects(Managed *that) -{ - StringObject *o = static_cast(that); - o->value.stringValue()->mark(); - Object::markObjects(that); -} - -DEFINE_MANAGED_VTABLE(StringCtor); - -StringCtor::StringCtor(ExecutionContext *scope) - : FunctionObject(scope, scope->engine->newIdentifier(QStringLiteral("String"))) -{ - vtbl = &static_vtbl; -} - -Value StringCtor::construct(Managed *m, Value *argv, int argc) -{ - Value value; - if (argc) - value = Value::fromString(argv[0].toString(m->engine()->current)); - else - value = Value::fromString(m->engine()->current, QString()); - return Value::fromObject(m->engine()->newStringObject(value)); -} - -Value StringCtor::call(Managed *m, const Value &, Value *argv, int argc) -{ - Value value; - if (argc) - value = Value::fromString(argv[0].toString(m->engine()->current)); - else - value = Value::fromString(m->engine()->current, QString()); - return value; -} - -void StringPrototype::init(ExecutionEngine *engine, const Value &ctor) -{ - ctor.objectValue()->defineReadonlyProperty(engine->id_prototype, Value::fromObject(this)); - ctor.objectValue()->defineReadonlyProperty(engine->id_length, Value::fromInt32(1)); - ctor.objectValue()->defineDefaultProperty(engine, QStringLiteral("fromCharCode"), method_fromCharCode, 1); - - defineDefaultProperty(engine, QStringLiteral("constructor"), ctor); - defineDefaultProperty(engine, QStringLiteral("toString"), method_toString); - defineDefaultProperty(engine, QStringLiteral("valueOf"), method_toString); // valueOf and toString are identical - defineDefaultProperty(engine, QStringLiteral("charAt"), method_charAt, 1); - defineDefaultProperty(engine, QStringLiteral("charCodeAt"), method_charCodeAt, 1); - defineDefaultProperty(engine, QStringLiteral("concat"), method_concat, 1); - defineDefaultProperty(engine, QStringLiteral("indexOf"), method_indexOf, 1); - defineDefaultProperty(engine, QStringLiteral("lastIndexOf"), method_lastIndexOf, 1); - defineDefaultProperty(engine, QStringLiteral("localeCompare"), method_localeCompare, 1); - defineDefaultProperty(engine, QStringLiteral("match"), method_match, 1); - defineDefaultProperty(engine, QStringLiteral("replace"), method_replace, 2); - defineDefaultProperty(engine, QStringLiteral("search"), method_search, 1); - defineDefaultProperty(engine, QStringLiteral("slice"), method_slice, 2); - defineDefaultProperty(engine, QStringLiteral("split"), method_split, 2); - defineDefaultProperty(engine, QStringLiteral("substr"), method_substr, 2); - defineDefaultProperty(engine, QStringLiteral("substring"), method_substring, 2); - defineDefaultProperty(engine, QStringLiteral("toLowerCase"), method_toLowerCase); - defineDefaultProperty(engine, QStringLiteral("toLocaleLowerCase"), method_toLocaleLowerCase); - defineDefaultProperty(engine, QStringLiteral("toUpperCase"), method_toUpperCase); - defineDefaultProperty(engine, QStringLiteral("toLocaleUpperCase"), method_toLocaleUpperCase); - defineDefaultProperty(engine, QStringLiteral("trim"), method_trim); -} - -static QString getThisString(ExecutionContext *ctx) -{ - String* str = 0; - Value thisObject = ctx->thisObject; - if (StringObject *thisString = thisObject.asStringObject()) - str = thisString->value.stringValue(); - else if (thisObject.isUndefined() || thisObject.isNull()) - ctx->throwTypeError(); - else - str = ctx->thisObject.toString(ctx); - return str->toQString(); -} - -static QString getThisString(ExecutionContext *context, Value thisObject) -{ - if (thisObject.isString()) - return thisObject.stringValue()->toQString(); - - String* str = 0; - if (StringObject *thisString = thisObject.asStringObject()) - str = thisString->value.stringValue(); - else if (thisObject.isUndefined() || thisObject.isNull()) - context->throwTypeError(); - else - str = thisObject.toString(context); - return str->toQString(); -} - -Value StringPrototype::method_toString(SimpleCallContext *context) -{ - if (context->thisObject.isString()) - return context->thisObject; - - StringObject *o = context->thisObject.asStringObject(); - if (!o) - context->throwTypeError(); - return o->value; -} - -Value StringPrototype::method_charAt(SimpleCallContext *context) -{ - const QString str = getThisString(context, context->thisObject); - - int pos = 0; - if (context->argumentCount > 0) - pos = (int) context->arguments[0].toInteger(); - - QString result; - if (pos >= 0 && pos < str.length()) - result += str.at(pos); - - return Value::fromString(context, result); -} - -Value StringPrototype::method_charCodeAt(SimpleCallContext *context) -{ - const QString str = getThisString(context, context->thisObject); - - int pos = 0; - if (context->argumentCount > 0) - pos = (int) context->arguments[0].toInteger(); - - - if (pos >= 0 && pos < str.length()) - return Value::fromInt32(str.at(pos).unicode()); - - return Value::fromDouble(qSNaN()); -} - -Value StringPrototype::method_concat(SimpleCallContext *context) -{ - QString value = getThisString(context, context->thisObject); - - for (int i = 0; i < context->argumentCount; ++i) { - Value v = __qmljs_to_string(context->arguments[i], context); - assert(v.isString()); - value += v.stringValue()->toQString(); - } - - return Value::fromString(context, value); -} - -Value StringPrototype::method_indexOf(SimpleCallContext *context) -{ - QString value = getThisString(context, context->thisObject); - - QString searchString; - if (context->argumentCount) - searchString = context->arguments[0].toString(context)->toQString(); - - int pos = 0; - if (context->argumentCount > 1) - pos = (int) context->arguments[1].toInteger(); - - int index = -1; - if (! value.isEmpty()) - index = value.indexOf(searchString, qMin(qMax(pos, 0), value.length())); - - return Value::fromDouble(index); -} - -Value StringPrototype::method_lastIndexOf(SimpleCallContext *context) -{ - const QString value = getThisString(context, context->thisObject); - - QString searchString; - if (context->argumentCount) { - Value v = __qmljs_to_string(context->arguments[0], context); - searchString = v.stringValue()->toQString(); - } - - Value posArg = context->argumentCount > 1 ? context->arguments[1] : Value::undefinedValue(); - double position = __qmljs_to_number(posArg); - if (std::isnan(position)) - position = +qInf(); - else - position = trunc(position); - - int pos = trunc(qMin(qMax(position, 0.0), double(value.length()))); - if (!searchString.isEmpty() && pos == value.length()) - --pos; - if (searchString.isNull() && pos == 0) - return Value::fromDouble(-1); - int index = value.lastIndexOf(searchString, pos); - return Value::fromDouble(index); -} - -Value StringPrototype::method_localeCompare(SimpleCallContext *context) -{ - const QString value = getThisString(context, context->thisObject); - const QString that = (context->argumentCount ? context->arguments[0] : Value::undefinedValue()).toString(context)->toQString(); - return Value::fromDouble(QString::localeAwareCompare(value, that)); -} - -Value StringPrototype::method_match(SimpleCallContext *context) -{ - if (context->thisObject.isUndefined() || context->thisObject.isNull()) - context->throwTypeError(); - - String *s = context->thisObject.toString(context); - - Value regexp = context->argumentCount ? context->arguments[0] : Value::undefinedValue(); - RegExpObject *rx = regexp.as(); - if (!rx) - rx = context->engine->regExpCtor.asFunctionObject()->construct(®exp, 1).as(); - - if (!rx) - // ### CHECK - context->throwTypeError(); - - bool global = rx->global; - - // ### use the standard builtin function, not the one that might be redefined in the proto - FunctionObject *exec = context->engine->regExpPrototype->get(context->engine->newString(QStringLiteral("exec")), 0).asFunctionObject(); - - Value arg = Value::fromString(s); - if (!global) - return exec->call(Value::fromObject(rx), &arg, 1); - - String *lastIndex = context->engine->newString(QStringLiteral("lastIndex")); - rx->put(lastIndex, Value::fromInt32(0)); - ArrayObject *a = context->engine->newArrayObject(); - - double previousLastIndex = 0; - uint n = 0; - while (1) { - Value result = exec->call(Value::fromObject(rx), &arg, 1); - if (result.isNull()) - break; - assert(result.isObject()); - double thisIndex = rx->get(lastIndex, 0).toInteger(); - if (previousLastIndex == thisIndex) { - previousLastIndex = thisIndex + 1; - rx->put(lastIndex, Value::fromDouble(previousLastIndex)); - } else { - previousLastIndex = thisIndex; - } - Value matchStr = result.objectValue()->getIndexed(0); - a->arraySet(n, matchStr); - ++n; - } - if (!n) - return Value::nullValue(); - - return Value::fromObject(a); - -} - -static QString makeReplacementString(const QString &input, const QString& replaceValue, uint* matchOffsets, int captureCount) -{ - QString result; - result.reserve(replaceValue.length()); - for (int i = 0; i < replaceValue.length(); ++i) { - if (replaceValue.at(i) == QLatin1Char('$') && i < replaceValue.length() - 1) { - char ch = replaceValue.at(++i).toLatin1(); - uint substStart = JSC::Yarr::offsetNoMatch; - uint substEnd = JSC::Yarr::offsetNoMatch; - if (ch == '$') { - result += ch; - continue; - } else if (ch == '&') { - substStart = matchOffsets[0]; - substEnd = matchOffsets[1]; - } else if (ch == '`') { - substStart = 0; - substEnd = matchOffsets[0]; - } else if (ch == '\'') { - substStart = matchOffsets[1]; - substEnd = input.length(); - } else if (ch >= '1' && ch <= '9') { - char capture = ch - '0'; - if (capture > 0 && capture < captureCount) { - substStart = matchOffsets[capture * 2]; - substEnd = matchOffsets[capture * 2 + 1]; - } - } else if (ch == '0' && i < replaceValue.length() - 1) { - int capture = (ch - '0') * 10; - ch = replaceValue.at(++i).toLatin1(); - capture += ch - '0'; - if (capture > 0 && capture < captureCount) { - substStart = matchOffsets[capture * 2]; - substEnd = matchOffsets[capture * 2 + 1]; - } - } - if (substStart != JSC::Yarr::offsetNoMatch && substEnd != JSC::Yarr::offsetNoMatch) - result += input.midRef(substStart, substEnd - substStart); - } else { - result += replaceValue.at(i); - } - } - return result; -} - -Value StringPrototype::method_replace(SimpleCallContext *ctx) -{ - QString string; - if (StringObject *thisString = ctx->thisObject.asStringObject()) - string = thisString->value.stringValue()->toQString(); - else - string = ctx->thisObject.toString(ctx)->toQString(); - - int numCaptures = 0; - QVarLengthArray matchOffsets; - int numStringMatches = 0; - - Value searchValue = ctx->argument(0); - RegExpObject *regExp = searchValue.as(); - if (regExp) { - uint offset = 0; - while (true) { - int oldSize = matchOffsets.size(); - matchOffsets.resize(matchOffsets.size() + regExp->value->captureCount() * 2); - if (regExp->value->match(string, offset, matchOffsets.data() + oldSize) == JSC::Yarr::offsetNoMatch) { - matchOffsets.resize(oldSize); - break; - } - if (!regExp->global) - break; - offset = qMax(offset + 1, matchOffsets[oldSize + 1]); - } - if (regExp->global) - regExp->lastIndexProperty(ctx)->value = Value::fromUInt32(0); - numStringMatches = matchOffsets.size() / (regExp->value->captureCount() * 2); - numCaptures = regExp->value->captureCount(); - } else { - numCaptures = 1; - QString searchString = searchValue.toString(ctx)->toQString(); - int idx = string.indexOf(searchString); - if (idx != -1) { - numStringMatches = 1; - matchOffsets.resize(2); - matchOffsets[0] = idx; - matchOffsets[1] = idx + searchString.length(); - } - } - - QString result = string; - Value replaceValue = ctx->argument(1); - if (FunctionObject* searchCallback = replaceValue.asFunctionObject()) { - int replacementDelta = 0; - int argc = numCaptures + 2; - Value *args = (Value*)alloca((numCaptures + 2) * sizeof(Value)); - for (int i = 0; i < numStringMatches; ++i) { - for (int k = 0; k < numCaptures; ++k) { - int idx = (i * numCaptures + k) * 2; - uint start = matchOffsets[idx]; - uint end = matchOffsets[idx + 1]; - Value entry = Value::undefinedValue(); - if (start != JSC::Yarr::offsetNoMatch && end != JSC::Yarr::offsetNoMatch) - entry = Value::fromString(ctx, string.mid(start, end - start)); - args[k] = entry; - } - uint matchStart = matchOffsets[i * numCaptures * 2]; - uint matchEnd = matchOffsets[i * numCaptures * 2 + 1]; - args[numCaptures] = Value::fromUInt32(matchStart); - args[numCaptures + 1] = Value::fromString(ctx, string); - Value replacement = searchCallback->call(Value::undefinedValue(), args, argc); - QString replacementString = replacement.toString(ctx)->toQString(); - result.replace(replacementDelta + matchStart, matchEnd - matchStart, replacementString); - replacementDelta += replacementString.length() - matchEnd + matchStart; - } - } else { - QString newString = replaceValue.toString(ctx)->toQString(); - int replacementDelta = 0; - - for (int i = 0; i < numStringMatches; ++i) { - int baseIndex = i * numCaptures * 2; - uint matchStart = matchOffsets[baseIndex]; - uint matchEnd = matchOffsets[baseIndex + 1]; - if (matchStart == JSC::Yarr::offsetNoMatch) - continue; - - QString replacement = makeReplacementString(string, newString, matchOffsets.data() + baseIndex, numCaptures); - result.replace(replacementDelta + matchStart, matchEnd - matchStart, replacement); - replacementDelta += replacement.length() - matchEnd + matchStart; - } - } - - return Value::fromString(ctx, result); -} - -Value StringPrototype::method_search(SimpleCallContext *ctx) -{ - QString string; - if (StringObject *thisString = ctx->thisObject.asStringObject()) - string = thisString->value.stringValue()->toQString(); - else - string = ctx->thisObject.toString(ctx)->toQString(); - - Value regExpValue = ctx->argument(0); - RegExpObject *regExp = regExpValue.as(); - if (!regExp) { - regExpValue = ctx->engine->regExpCtor.asFunctionObject()->construct(®ExpValue, 1); - regExp = regExpValue.as(); - } - uint* matchOffsets = (uint*)alloca(regExp->value->captureCount() * 2 * sizeof(uint)); - uint result = regExp->value->match(string, /*offset*/0, matchOffsets); - if (result == JSC::Yarr::offsetNoMatch) - return Value::fromInt32(-1); - return Value::fromUInt32(result); -} - -Value StringPrototype::method_slice(SimpleCallContext *ctx) -{ - const QString text = getThisString(ctx); - const double length = text.length(); - - double start = ctx->argument(0).toInteger(); - double end = ctx->argument(1).isUndefined() - ? length : ctx->argument(1).toInteger(); - - if (start < 0) - start = qMax(length + start, 0.); - else - start = qMin(start, length); - - if (end < 0) - end = qMax(length + end, 0.); - else - end = qMin(end, length); - - const int intStart = int(start); - const int intEnd = int(end); - - int count = qMax(0, intEnd - intStart); - return Value::fromString(ctx, text.mid(intStart, count)); -} - -Value StringPrototype::method_split(SimpleCallContext *ctx) -{ - QString text; - if (StringObject *thisObject = ctx->thisObject.asStringObject()) - text = thisObject->value.stringValue()->toQString(); - else - text = ctx->thisObject.toString(ctx)->toQString(); - - Value separatorValue = ctx->argumentCount > 0 ? ctx->argument(0) : Value::undefinedValue(); - Value limitValue = ctx->argumentCount > 1 ? ctx->argument(1) : Value::undefinedValue(); - - ArrayObject* array = ctx->engine->newArrayObject(); - Value result = Value::fromObject(array); - - if (separatorValue.isUndefined()) { - if (limitValue.isUndefined()) { - array->push_back(Value::fromString(ctx, text)); - return result; - } - return Value::fromString(ctx, text.left(limitValue.toInteger())); - } - - uint limit = limitValue.isUndefined() ? UINT_MAX : limitValue.toUInt32(); - - if (limit == 0) - return result; - - if (RegExpObject* re = separatorValue.as()) { - if (re->value->pattern().isEmpty()) { - re = 0; - separatorValue = Value::fromString(ctx, QString()); - } - } - - if (RegExpObject* re = separatorValue.as()) { - uint offset = 0; - uint* matchOffsets = (uint*)alloca(re->value->captureCount() * 2 * sizeof(uint)); - while (true) { - uint result = re->value->match(text, offset, matchOffsets); - if (result == JSC::Yarr::offsetNoMatch) - break; - - array->push_back(Value::fromString(ctx, text.mid(offset, matchOffsets[0] - offset))); - offset = qMax(offset + 1, matchOffsets[1]); - - if (array->arrayLength() >= limit) - break; - - for (int i = 1; i < re->value->captureCount(); ++i) { - uint start = matchOffsets[i * 2]; - uint end = matchOffsets[i * 2 + 1]; - array->push_back(Value::fromString(ctx, text.mid(start, end - start))); - if (array->arrayLength() >= limit) - break; - } - } - if (array->arrayLength() < limit) - array->push_back(Value::fromString(ctx, text.mid(offset))); - } else { - QString separator = separatorValue.toString(ctx)->toQString(); - if (separator.isEmpty()) { - for (uint i = 0; i < qMin(limit, uint(text.length())); ++i) - array->push_back(Value::fromString(ctx, text.mid(i, 1))); - return result; - } - - int start = 0; - int end; - while ((end = text.indexOf(separator, start)) != -1) { - array->push_back(Value::fromString(ctx, text.mid(start, end - start))); - start = end + separator.size(); - if (array->arrayLength() >= limit) - break; - } - if (array->arrayLength() < limit && start != -1) - array->push_back(Value::fromString(ctx, text.mid(start))); - } - return result; -} - -Value StringPrototype::method_substr(SimpleCallContext *context) -{ - const QString value = getThisString(context, context->thisObject); - - double start = 0; - if (context->argumentCount > 0) - start = context->arguments[0].toInteger(); - - double length = +qInf(); - if (context->argumentCount > 1) - length = context->arguments[1].toInteger(); - - double count = value.length(); - if (start < 0) - start = qMax(count + start, 0.0); - - length = qMin(qMax(length, 0.0), count - start); - - qint32 x = Value::toInt32(start); - qint32 y = Value::toInt32(length); - return Value::fromString(context, value.mid(x, y)); -} - -Value StringPrototype::method_substring(SimpleCallContext *context) -{ - QString value = getThisString(context, context->thisObject); - int length = value.length(); - - double start = 0; - double end = length; - - if (context->argumentCount > 0) - start = context->arguments[0].toInteger(); - - Value endValue = context->argumentCount > 1 ? context->arguments[1] : Value::undefinedValue(); - if (!endValue.isUndefined()) - end = endValue.toInteger(); - - if (std::isnan(start) || start < 0) - start = 0; - - if (std::isnan(end) || end < 0) - end = 0; - - if (start > length) - start = length; - - if (end > length) - end = length; - - if (start > end) { - double was = start; - start = end; - end = was; - } - - qint32 x = (int)start; - qint32 y = (int)(end - start); - return Value::fromString(context, value.mid(x, y)); -} - -Value StringPrototype::method_toLowerCase(SimpleCallContext *ctx) -{ - QString value = getThisString(ctx); - return Value::fromString(ctx, value.toLower()); -} - -Value StringPrototype::method_toLocaleLowerCase(SimpleCallContext *ctx) -{ - return method_toLowerCase(ctx); -} - -Value StringPrototype::method_toUpperCase(SimpleCallContext *ctx) -{ - QString value = getThisString(ctx); - return Value::fromString(ctx, value.toUpper()); -} - -Value StringPrototype::method_toLocaleUpperCase(SimpleCallContext *ctx) -{ - return method_toUpperCase(ctx); -} - -Value StringPrototype::method_fromCharCode(SimpleCallContext *context) -{ - QString str(context->argumentCount, Qt::Uninitialized); - QChar *ch = str.data(); - for (int i = 0; i < context->argumentCount; ++i) { - *ch = QChar(context->arguments[i].toUInt16()); - ++ch; - } - return Value::fromString(context, str); -} - -Value StringPrototype::method_trim(SimpleCallContext *ctx) -{ - if (ctx->thisObject.isNull() || ctx->thisObject.isUndefined()) - ctx->throwTypeError(); - - QString s = __qmljs_to_string(ctx->thisObject, ctx).stringValue()->toQString(); - const QChar *chars = s.constData(); - int start, end; - for (start = 0; start < s.length(); ++start) { - if (!chars[start].isSpace() && chars[start].unicode() != 0xfeff) - break; - } - for (end = s.length() - 1; end >= start; --end) { - if (!chars[end].isSpace() && chars[end].unicode() != 0xfeff) - break; - } - - return Value::fromString(ctx, QString(chars + start, end - start + 1)); -} diff --git a/src/qml/qml/v4/qv4stringobject_p.h b/src/qml/qml/v4/qv4stringobject_p.h deleted file mode 100644 index 0ef6596235..0000000000 --- a/src/qml/qml/v4/qv4stringobject_p.h +++ /dev/null @@ -1,110 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QV4STRINGOBJECT_P_H -#define QV4STRINGOBJECT_P_H - -#include "qv4object_p.h" -#include "qv4functionobject_p.h" -#include - -QT_BEGIN_NAMESPACE - -namespace QV4 { - -struct StringObject: Object { - Q_MANAGED - - Value value; - mutable Property tmpProperty; - StringObject(ExecutionEngine *engine, const Value &value); - - Property *getIndex(uint index) const; - - static bool deleteIndexedProperty(Managed *m, uint index); - -protected: - static Property *advanceIterator(Managed *m, ObjectIterator *it, String **name, uint *index, PropertyAttributes *attrs); - static void markObjects(Managed *that); -}; - -struct StringCtor: FunctionObject -{ - StringCtor(ExecutionContext *scope); - - static Value construct(Managed *m, Value *args, int argc); - static Value call(Managed *that, const Value &, Value *, int); - -protected: - static const ManagedVTable static_vtbl; -}; - -struct StringPrototype: StringObject -{ - StringPrototype(ExecutionEngine *engine): StringObject(engine, Value::fromString(engine, QString())) {} - void init(ExecutionEngine *engine, const Value &ctor); - - static Value method_toString(SimpleCallContext *context); - static Value method_charAt(SimpleCallContext *context); - static Value method_charCodeAt(SimpleCallContext *context); - static Value method_concat(SimpleCallContext *context); - static Value method_indexOf(SimpleCallContext *context); - static Value method_lastIndexOf(SimpleCallContext *context); - static Value method_localeCompare(SimpleCallContext *context); - static Value method_match(SimpleCallContext *context); - static Value method_replace(SimpleCallContext *ctx); - static Value method_search(SimpleCallContext *ctx); - static Value method_slice(SimpleCallContext *ctx); - static Value method_split(SimpleCallContext *ctx); - static Value method_substr(SimpleCallContext *context); - static Value method_substring(SimpleCallContext *context); - static Value method_toLowerCase(SimpleCallContext *ctx); - static Value method_toLocaleLowerCase(SimpleCallContext *ctx); - static Value method_toUpperCase(SimpleCallContext *ctx); - static Value method_toLocaleUpperCase(SimpleCallContext *ctx); - static Value method_fromCharCode(SimpleCallContext *context); - static Value method_trim(SimpleCallContext *ctx); -}; - -} - -QT_END_NAMESPACE - -#endif // QV4ECMAOBJECTS_P_H diff --git a/src/qml/qml/v4/qv4syntaxchecker.cpp b/src/qml/qml/v4/qv4syntaxchecker.cpp deleted file mode 100644 index a4f7423cd7..0000000000 --- a/src/qml/qml/v4/qv4syntaxchecker.cpp +++ /dev/null @@ -1,123 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4syntaxchecker_p.h" - -QT_BEGIN_NAMESPACE - -using namespace QQmlJS; - -SyntaxChecker::SyntaxChecker() - : Lexer(&m_engine) - , m_stateStack(128) -{ -} - -void QQmlJS::SyntaxChecker::clearText() -{ - m_code.clear(); - m_tokens.clear(); -} - -void SyntaxChecker::appendText(const QString &text) -{ - m_code += text; -} - -QString SyntaxChecker::text() const -{ - return m_code; -} - -bool SyntaxChecker::canEvaluate() -{ - int yyaction = 0; - int yytoken = -1; - int yytos = -1; - - setCode(m_code, 1); - - m_tokens.clear(); - m_tokens.append(T_FEED_JS_PROGRAM); - - do { - if (++yytos == m_stateStack.size()) - m_stateStack.resize(m_stateStack.size() * 2); - - m_stateStack[yytos] = yyaction; - -again: - if (yytoken == -1 && action_index[yyaction] != -TERMINAL_COUNT) { - if (m_tokens.isEmpty()) - yytoken = lex(); - else - yytoken = m_tokens.takeFirst(); - } - - yyaction = t_action(yyaction, yytoken); - if (yyaction > 0) { - if (yyaction == ACCEPT_STATE) { - --yytos; - return true; - } - yytoken = -1; - } else if (yyaction < 0) { - const int ruleno = -yyaction - 1; - yytos -= rhs[ruleno]; - yyaction = nt_action(m_stateStack[yytos], lhs[ruleno] - TERMINAL_COUNT); - } - } while (yyaction); - - const int errorState = m_stateStack[yytos]; - if (t_action(errorState, T_AUTOMATIC_SEMICOLON) && canInsertAutomaticSemicolon(yytoken)) { - yyaction = errorState; - m_tokens.prepend(yytoken); - yytoken = T_SEMICOLON; - goto again; - } - - if (yytoken != EOF_SYMBOL) - return true; - - return false; -} - -QT_END_NAMESPACE diff --git a/src/qml/qml/v4/qv4syntaxchecker_p.h b/src/qml/qml/v4/qv4syntaxchecker_p.h deleted file mode 100644 index bdb88b0525..0000000000 --- a/src/qml/qml/v4/qv4syntaxchecker_p.h +++ /dev/null @@ -1,77 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QV4SYNTAXCHECKER_P_H -#define QV4SYNTAXCHECKER_P_H - -#include -#include - -#include -#include -#include - -QT_BEGIN_NAMESPACE - -namespace QQmlJS { - -class SyntaxChecker: Lexer -{ -public: - SyntaxChecker(); - - QString text() const; - void clearText(); - void appendText(const QString &text); - - bool canEvaluate(); - -private: - Engine m_engine; - QVector m_stateStack; - QList m_tokens; - QString m_code; -}; - -} // end of QQmlJS namespace - -QT_END_NAMESPACE - -#endif // QV4SYNTAXCHECKER_P_H diff --git a/src/qml/qml/v4/qv4unwindhelper.cpp b/src/qml/qml/v4/qv4unwindhelper.cpp deleted file mode 100644 index beb5132626..0000000000 --- a/src/qml/qml/v4/qv4unwindhelper.cpp +++ /dev/null @@ -1,82 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include - -#include - -#if CPU(X86_64) && (OS(LINUX) || OS(MAC_OS_X)) -# define USE_DW2_HELPER -#elif CPU(X86) && COMPILER(GCC) -# define USE_DW2_HELPER -#elif CPU(ARM) && (OS(LINUX) || OS(QNX)) -# define USE_ARM_HELPER -#elif OS(WINDOWS) - // SJLJ will unwind on Windows -# define USE_NULL_HELPER -#elif OS(IOS) - // SJLJ will unwind on iOS -# define USE_NULL_HELPER -#else -# warning "Unsupported/untested platform!" -# define USE_NULL_HELPER -#endif - -#ifdef USE_DW2_HELPER -# include -#endif // USE_DW2_HELPER - -#ifdef USE_ARM_HELPER -# include -#endif // USE_ARM_HELPER - -QT_BEGIN_NAMESPACE - -#ifdef USE_NULL_HELPER -using namespace QV4; -void UnwindHelper::prepareForUnwind(ExecutionContext *) {} -void UnwindHelper::registerFunction(Function *function) {Q_UNUSED(function);} -void UnwindHelper::registerFunctions(const QVector &functions) {Q_UNUSED(functions);} -void UnwindHelper::deregisterFunction(Function *function) {Q_UNUSED(function);} -void UnwindHelper::deregisterFunctions(const QVector &functions) {Q_UNUSED(functions);} -#endif // USE_NULL_HELPER - -QT_END_NAMESPACE diff --git a/src/qml/qml/v4/qv4unwindhelper_p-arm.h b/src/qml/qml/v4/qv4unwindhelper_p-arm.h deleted file mode 100644 index dd1f1e4856..0000000000 --- a/src/qml/qml/v4/qv4unwindhelper_p-arm.h +++ /dev/null @@ -1,233 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QV4UNWINDHELPER_PDW2_H -#define QV4UNWINDHELPER_PDW2_H - -#include "qv4unwindhelper_p.h" -#include "qv4functionobject_p.h" -#include "qv4function_p.h" -#include - -#include -#include - -#define __USE_GNU -#include - -#if USE(LIBUNWIND_DEBUG) -#include -#include -#endif - -QT_BEGIN_NAMESPACE - -namespace QV4 { - -static void *removeThumbBit(void *addr) -{ - return reinterpret_cast(reinterpret_cast(addr) & ~1u); -} - -static QMutex functionProtector; -static QMap allFunctions; - -static Function *lookupFunction(void *pc) -{ - quintptr key = reinterpret_cast(pc); - QMap::ConstIterator it = allFunctions.lowerBound(key); - if (it != allFunctions.begin() && allFunctions.count() > 0) - --it; - if (it == allFunctions.end()) - return 0; - - quintptr codeStart = reinterpret_cast(removeThumbBit((*it)->codeRef.code().executableAddress())); - if (key < codeStart || key >= codeStart + (*it)->codeSize) - return 0; - return *it; -} - - -/* Program: -vsp = r4 (REG_TO_SP r4) -vsp -= 8 * 4 -- > vsp = vsp - (7 << 2) - 4 -pop r12, r10, r9, r8, r7, r6, r5, r4 -pop r4 -pop lr -pop r0, r1, r2, r3 -*/ - -#define REG_TO_SP 0b10010000 -#define VSP_MINUS 0b01000000 -#define POP_REG_MULTI 0b10000000 -#define POP_R4_MULTI 0b10100000 -#define POP_R4_R14_MULTI 0b10101000 -#define POP_R0_TO_R3 0b10110001 -#define FINISH 0b10110000 - -#define MK_UW_WORD(first, second, third, fourth) \ - (((first) << 24) | \ - ((second) << 16) | \ - ((third) << 8) | \ - (fourth)) - -static unsigned int extbl[] = { - MK_UW_WORD(0x80 | // High bit set to indicate that this isn't a PREL31 - 2, // Choose personality routine #2 - 2, // Number of 4 byte words used to encode remaining unwind instructions - REG_TO_SP | 4, // Encoded program from above. - VSP_MINUS | 7), - MK_UW_WORD(POP_REG_MULTI | 1, 0b01111111, - POP_R4_R14_MULTI, - POP_R0_TO_R3), - MK_UW_WORD(0b00001111, - FINISH, - FINISH, - FINISH) -}; - -static unsigned write_prel31(unsigned *addr, void *ptr) -{ - int delta = (char *)ptr - (char*)addr; - if (delta < 0) - delta |= (1 << 30); - else - delta &= ~(1 << 30); - *addr = ((unsigned)delta) & 0x7fffffffU; -} - -void UnwindHelper::deregisterFunction(Function *function) -{ - QMutexLocker locker(&functionProtector); - allFunctions.remove(reinterpret_cast(function->code)); -} - -void UnwindHelper::deregisterFunctions(const QVector &functions) -{ - QMutexLocker locker(&functionProtector); - foreach (Function *f, functions) - allFunctions.remove(reinterpret_cast(f->code)); -} - -void UnwindHelper::registerFunction(Function *function) -{ - QMutexLocker locker(&functionProtector); - allFunctions.insert(reinterpret_cast(function->code), function); -} - -void UnwindHelper::registerFunctions(const QVector &functions) -{ - QMutexLocker locker(&functionProtector); - foreach (Function *f, functions) - allFunctions.insert(reinterpret_cast(f->code), f); -} - -void UnwindHelper::prepareForUnwind(ExecutionContext *) -{ -} - -int UnwindHelper::unwindInfoSize() -{ - return 2 * sizeof(unsigned int) // 2 extbl entries - + sizeof(extbl); -} - -void UnwindHelper::writeARMUnwindInfo(void *codeAddr, int codeSize) -{ - unsigned int *exidx = (unsigned int *)((char *)codeAddr + codeSize); - - unsigned char *exprog = (unsigned char *)((unsigned char *)codeAddr + codeSize + 8); - - write_prel31(exidx, codeAddr); - exidx[1] = 4; // PREL31 offset to extbl, which follows right afterwards - - memcpy(exprog, extbl, sizeof(extbl)); - -#if USE(LIBUNWIND_DEBUG) - unw_dyn_info_t *info = (unw_dyn_info_t*)malloc(sizeof(unw_dyn_info_t)); - info->start_ip = (unw_word_t)codeAddr; - info->end_ip = info->start_ip + codeSize; - info->gp = 0; - info->format = UNW_INFO_FORMAT_ARM_EXIDX; - info->u.rti.name_ptr = 0; - info->u.rti.segbase = 0; - info->u.rti.table_len = 8; - info->u.rti.table_data = (unw_word_t)exidx; - _U_dyn_register(info); -#endif -} - -} - -QT_END_NAMESPACE - -#if defined(Q_OS_ANDROID) -extern "C" void *dl_unwind_find_exidx(void *pc, int *entryCount); -#endif - -extern "C" Q_DECL_EXPORT void *__gnu_Unwind_Find_exidx(void *pc, int *entryCount) -{ -#if !defined(Q_OS_ANDROID) - typedef void *(*Old_Unwind_Find_exidx)(void*, int*); - static Old_Unwind_Find_exidx oldFunction = 0; - if (!oldFunction) - oldFunction = (Old_Unwind_Find_exidx)dlsym(RTLD_NEXT, "__gnu_Unwind_Find_exidx"); -#endif - - { - QMutexLocker locker(&QT_PREPEND_NAMESPACE(QV4::functionProtector)); - QV4::Function *function = QT_PREPEND_NAMESPACE(QV4::lookupFunction(pc)); - if (function) { - *entryCount = 1; - void * codeStart = QT_PREPEND_NAMESPACE(QV4::removeThumbBit(function->codeRef.code().executableAddress())); - // At the end of the function we store our synthetic exception table entry. - return (char *)codeStart + function->codeSize; - } - } - -#if defined(Q_OS_ANDROID) - return dl_unwind_find_exidx(pc, entryCount); -#else - return oldFunction(pc, entryCount); -#endif -} - -#endif // QV4UNWINDHELPER_PDW2_H diff --git a/src/qml/qml/v4/qv4unwindhelper_p-dw2.h b/src/qml/qml/v4/qv4unwindhelper_p-dw2.h deleted file mode 100644 index 57615f0999..0000000000 --- a/src/qml/qml/v4/qv4unwindhelper_p-dw2.h +++ /dev/null @@ -1,191 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QV4UNWINDHELPER_PDW2_H -#define QV4UNWINDHELPER_PDW2_H - -#include "qv4unwindhelper_p.h" -#include "qv4functionobject_p.h" -#include "qv4function_p.h" -#include -#include -#include - -#include -#include - -QT_BEGIN_NAMESPACE - -namespace QV4 { - -namespace { -#if CPU(X86_64) -// Generated by fdegen -static const unsigned char cie_fde_data[] = { - 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x1, 0x0, 0x8, 0x78, 0x10, 0xc, 0x7, 0x8, - 0x90, 0x1, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, - 0x18, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x41, 0x13, 0x7e, 0x86, - 0x2, 0x43, 0xd, 0x6, 0x8c, 0x3, 0x8e, 0x4, - 0x0, 0x0, 0x0, 0x0 -}; -static const int fde_offset = 20; -static const int initial_location_offset = 28; -static const int address_range_offset = 36; -#elif CPU(X86) -static const unsigned char cie_fde_data[] = { - 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x1, 0x0, 0x4, 0x7c, 0x8, 0xc, 0x4, 0x4, - 0x88, 0x1, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, - 0x18, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x41, 0x13, 0x7e, 0x85, - 0x2, 0x43, 0xd, 0x5, 0x86, 0x3, 0x87, 0x4, - 0x0, 0x0, 0x0, 0x0, -}; -static const int fde_offset = 20; -static const int initial_location_offset = 28; -static const int address_range_offset = 32; -#endif - -void writeIntPtrValue(unsigned char *addr, intptr_t val) -{ - addr[0] = (val >> 0) & 0xff; - addr[1] = (val >> 8) & 0xff; - addr[2] = (val >> 16) & 0xff; - addr[3] = (val >> 24) & 0xff; -#if QT_POINTER_SIZE == 8 - addr[4] = (val >> 32) & 0xff; - addr[5] = (val >> 40) & 0xff; - addr[6] = (val >> 48) & 0xff; - addr[7] = (val >> 56) & 0xff; -#endif -} -} // anonymous namespace - -extern "C" void __register_frame(void *fde); -extern "C" void __deregister_frame(void *fde); - -struct UnwindInfo : public ExecutableAllocator::PlatformUnwindInfo -{ - UnwindInfo(const QByteArray &cieFde); - virtual ~UnwindInfo(); - QByteArray data; -}; - -UnwindInfo::UnwindInfo(const QByteArray &cieFde) - : data(cieFde) -{ - __register_frame(data.data() + fde_offset); -} - -UnwindInfo::~UnwindInfo() -{ - __deregister_frame(data.data() + fde_offset); -} - -static void ensureUnwindInfo(Function *f) -{ - if (!f->codeRef) - return; // Not a JIT generated function - - JSC::ExecutableMemoryHandle *handle = f->codeRef.executableMemory(); - if (!handle) - return; - ExecutableAllocator::ChunkOfPages *chunk = handle->chunk(); - - // Already registered? - if (chunk->unwindInfo) - return; - - QByteArray info; - info.resize(sizeof(cie_fde_data)); - - unsigned char *cie_and_fde = reinterpret_cast(info.data()); - memcpy(cie_and_fde, cie_fde_data, sizeof(cie_fde_data)); - - intptr_t ptr = static_cast(chunk->pages->base()) - static_cast(0); - writeIntPtrValue(cie_and_fde + initial_location_offset, ptr); - - writeIntPtrValue(cie_and_fde + address_range_offset, chunk->pages->size()); - - chunk->unwindInfo = new UnwindInfo(info); -} - -void UnwindHelper::prepareForUnwind(ExecutionContext *context) -{ - for (ExecutionContext *ctx = context; ctx; ctx = ctx->parent) { - if (CallContext *callCtx = ctx->asCallContext()) - if (FunctionObject *fobj = callCtx->function) - if (Function *fun = fobj->function) - ensureUnwindInfo(fun); - for (ExecutionContext::EvalCode *code = ctx->currentEvalCode; - code; code = code->next) - ensureUnwindInfo(code->function); - } - - if (context->engine->globalCode) - ensureUnwindInfo(context->engine->globalCode); -} - -void UnwindHelper::registerFunction(Function *) -{ -} - -void UnwindHelper::registerFunctions(const QVector&) -{ -} - -void UnwindHelper::deregisterFunction(Function *) -{ -} - -void UnwindHelper::deregisterFunctions(const QVector &) -{ -} - -} - -QT_END_NAMESPACE - -#endif // QV4UNWINDHELPER_PDW2_H diff --git a/src/qml/qml/v4/qv4unwindhelper_p.h b/src/qml/qml/v4/qv4unwindhelper_p.h deleted file mode 100644 index 9ef564449a..0000000000 --- a/src/qml/qml/v4/qv4unwindhelper_p.h +++ /dev/null @@ -1,72 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QV4UNWINDHELPER_H -#define QV4UNWINDHELPER_H - -#include - -QT_BEGIN_NAMESPACE - -namespace QV4 { - -struct Function; -struct ExecutionContext; - -class UnwindHelper -{ -public: - static void prepareForUnwind(ExecutionContext *ctx); - static void registerFunction(Function *function); - static void registerFunctions(const QVector &functions); - static void deregisterFunction(Function *function); - static void deregisterFunctions(const QVector &functions); -#ifdef Q_PROCESSOR_ARM - static int unwindInfoSize(); - static void writeARMUnwindInfo(void *codeAddr, int codeSize); -#endif -}; - -} - -QT_END_NAMESPACE - -#endif // QV4UNWINDHELPER_H diff --git a/src/qml/qml/v4/qv4util_p.h b/src/qml/qml/v4/qv4util_p.h deleted file mode 100644 index dbd9f89faa..0000000000 --- a/src/qml/qml/v4/qv4util_p.h +++ /dev/null @@ -1,74 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QV4UTIL_H -#define QV4UTIL_H - -#include "qv4global_p.h" - -QT_BEGIN_NAMESPACE - -namespace QV4 { - -template -struct TemporaryAssignment -{ - TemporaryAssignment(T &var, const T& temporaryValue) - : variable(var) - , savedValue(var) - { - variable = temporaryValue; - } - ~TemporaryAssignment() - { - variable = savedValue; - } - T &variable; - T savedValue; -private: - TemporaryAssignment(const TemporaryAssignment&); - TemporaryAssignment operator=(const TemporaryAssignment&); -}; - -} - -QT_END_NAMESPACE - -#endif // QV4UTIL_H diff --git a/src/qml/qml/v4/qv4value.cpp b/src/qml/qml/v4/qv4value.cpp deleted file mode 100644 index a41262f12f..0000000000 --- a/src/qml/qml/v4/qv4value.cpp +++ /dev/null @@ -1,403 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#include -#include -#include -#include "qv4mm_p.h" -#include "qv4exception_p.h" - -#include - -using namespace QV4; - -int Value::toUInt16() const -{ - if (isConvertibleToInt()) - return (ushort)(uint)integerValue(); - - double number = __qmljs_to_number(*this); - - double D16 = 65536.0; - if ((number >= 0 && number < D16)) - return static_cast(number); - - if (!std::isfinite(number)) - return +0; - - double d = ::floor(::fabs(number)); - if (std::signbit(number)) - d = -d; - - number = ::fmod(d , D16); - - if (number < 0) - number += D16; - - return (unsigned short)number; -} - -double Value::toInteger() const -{ - if (isConvertibleToInt()) - return int_32; - - return Value::toInteger(__qmljs_to_number(*this)); -} - -double Value::toNumber() const -{ - return __qmljs_to_number(*this); -} - -QString Value::toQString() const -{ - switch (type()) { - case Value::Undefined_Type: - return QStringLiteral("undefined"); - case Value::Null_Type: - return QStringLiteral("null"); - case Value::Boolean_Type: - if (booleanValue()) - return QStringLiteral("true"); - else - return QStringLiteral("false"); - case Value::String_Type: - return stringValue()->toQString(); - case Value::Object_Type: { - ExecutionContext *ctx = objectValue()->internalClass->engine->current; - try { - Value prim = __qmljs_to_primitive(*this, STRING_HINT); - if (prim.isPrimitive()) - return prim.toQString(); - } catch (Exception &e) { - e.accept(ctx); - try { - Value prim = __qmljs_to_primitive(e.value(), STRING_HINT); - if (prim.isPrimitive()) - return prim.toQString(); - } catch(Exception &e) { - e.accept(ctx); - } - } - return QString(); - } - case Value::Integer_Type: { - QString str; - __qmljs_numberToString(&str, (double)int_32, 10); - return str; - } - default: { // double - QString str; - __qmljs_numberToString(&str, doubleValue(), 10); - return str; - } - } // switch -} - -bool Value::sameValue(Value other) const { - if (val == other.val) - return true; - if (isString() && other.isString()) - return stringValue()->isEqualTo(other.stringValue()); - if (isInteger()) - return int_32 ? (double(int_32) == other.dbl) : (other.val == 0); - if (other.isInteger()) - return other.int_32 ? (dbl == double(other.int_32)) : (val == 0); - return false; -} - -Value Value::fromString(ExecutionContext *ctx, const QString &s) -{ - return fromString(ctx->engine->newString(s)); -} - -Value Value::fromString(ExecutionEngine *engine, const QString &s) -{ - return fromString(engine->newString(s)); -} - - -int Value::toInt32(double number) -{ - const double D32 = 4294967296.0; - const double D31 = D32 / 2.0; - - if ((number >= -D31 && number < D31)) - return static_cast(number); - - - if (!std::isfinite(number)) - return 0; - - double d = ::floor(::fabs(number)); - if (std::signbit(number)) - d = -d; - - number = ::fmod(d , D32); - - if (number < -D31) - number += D32; - else if (number >= D31) - number -= D32; - - return int(number); -} - -unsigned int Value::toUInt32(double number) -{ - const double D32 = 4294967296.0; - if ((number >= 0 && number < D32)) - return static_cast(number); - - if (!std::isfinite(number)) - return +0; - - double d = ::floor(::fabs(number)); - if (std::signbit(number)) - d = -d; - - number = ::fmod(d , D32); - - if (number < 0) - number += D32; - - return unsigned(number); -} - -double Value::toInteger(double number) -{ - if (std::isnan(number)) - return +0; - else if (! number || std::isinf(number)) - return number; - const double v = floor(fabs(number)); - return std::signbit(number) ? -v : v; -} - -String *Value::toString(ExecutionContext *ctx) const -{ - if (isString()) - return stringValue(); - return __qmljs_convert_to_string(ctx, *this); -} - -Value Value::property(ExecutionContext *ctx, String *name) const -{ - return isObject() ? objectValue()->get(name) : undefinedValue(); -} - - -PersistentValue::PersistentValue(const Value &val) - : d(new PersistentValuePrivate(val)) -{ -} - -PersistentValue::PersistentValue(const PersistentValue &other) - : d(other.d) -{ - if (d) - d->ref(); -} - -PersistentValue &PersistentValue::operator=(const PersistentValue &other) -{ - if (d == other.d) - return *this; - - // the memory manager cleans up those with a refcount of 0 - - if (d) - d->deref(); - d = other.d; - if (d) - d->ref(); - - return *this; -} - -PersistentValue &PersistentValue::operator =(const Value &other) -{ - if (!d) { - d = new PersistentValuePrivate(other); - return *this; - } - d = d->detach(other); - return *this; -} - -PersistentValue::~PersistentValue() -{ - if (d) - d->deref(); -} - -WeakValue::WeakValue(const Value &val) - : d(new PersistentValuePrivate(val, /*engine*/0, /*weak*/true)) -{ -} - -WeakValue::WeakValue(const WeakValue &other) - : d(other.d) -{ - if (d) - d->ref(); -} - -WeakValue &WeakValue::operator=(const WeakValue &other) -{ - if (d == other.d) - return *this; - - // the memory manager cleans up those with a refcount of 0 - - if (d) - d->deref(); - d = other.d; - if (d) - d->ref(); - - return *this; -} - -WeakValue &WeakValue::operator =(const Value &other) -{ - if (!d) { - d = new PersistentValuePrivate(other, /*engine*/0, /*weak*/true); - return *this; - } - d = d->detach(other, /*weak*/true); - return *this; -} - - -WeakValue::~WeakValue() -{ - if (d) - d->deref(); -} - -void WeakValue::markOnce() -{ - if (!d) - return; - Managed *m = d->value.asManaged(); - if (!m) - return; - m->mark(); -} - -PersistentValuePrivate::PersistentValuePrivate(const Value &v, ExecutionEngine *e, bool weak) - : value(v) - , refcount(1) - , prev(0) - , next(0) - , engine(e) -{ - if (!engine) { - Managed *m = v.asManaged(); - if (!m) - return; - - engine = m->engine(); - } - if (engine) { - PersistentValuePrivate **listRoot = weak ? &engine->memoryManager->m_weakValues : &engine->memoryManager->m_persistentValues; - - prev = listRoot; - next = *listRoot; - *prev = this; - if (next) - next->prev = &this->next; - } -} - -PersistentValuePrivate::~PersistentValuePrivate() -{ -} - -void PersistentValuePrivate::removeFromList() -{ - if (prev) { - if (next) - next->prev = prev; - *prev = next; - next = 0; - prev = 0; - } -} - -void PersistentValuePrivate::deref() -{ - // if engine is not 0, they are registered with the memory manager - // and will get cleaned up in the next gc run - if (!--refcount) { - removeFromList(); - delete this; - } -} - -PersistentValuePrivate *PersistentValuePrivate::detach(const QV4::Value &value, bool weak) -{ - if (refcount == 1) { - this->value = value; - - Managed *m = value.asManaged(); - if (!prev) { - if (m) { - ExecutionEngine *engine = m->engine(); - if (engine) { - PersistentValuePrivate **listRoot = weak ? &engine->memoryManager->m_weakValues : &engine->memoryManager->m_persistentValues; - prev = listRoot; - next = *listRoot; - *prev = this; - if (next) - next->prev = &this->next; - } - } - } else if (!m) - removeFromList(); - - return this; - } - --refcount; - return new PersistentValuePrivate(value, engine, weak); -} - diff --git a/src/qml/qml/v4/qv4value_def_p.h b/src/qml/qml/v4/qv4value_def_p.h deleted file mode 100644 index a44af16b6a..0000000000 --- a/src/qml/qml/v4/qv4value_def_p.h +++ /dev/null @@ -1,282 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QV4VALUE_DEF_P_H -#define QV4VALUE_DEF_P_H - -#include -#include "qv4global_p.h" - -QT_BEGIN_NAMESPACE - -namespace QV4 { - -typedef uint Bool; - -struct Q_QML_EXPORT Value -{ - union { - quint64 val; - double dbl; - struct { -#if Q_BYTE_ORDER != Q_LITTLE_ENDIAN - uint tag; -#endif - union { - uint uint_32; - int int_32; -#if QT_POINTER_SIZE == 4 - Managed *m; - Object *o; - String *s; -#endif - }; -#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN - uint tag; -#endif - }; - }; - - enum Masks { - NaN_Mask = 0x7ff80000, - NotDouble_Mask = 0x7ffc0000, - Type_Mask = 0xffff8000, - Immediate_Mask = NotDouble_Mask | 0x00008000, - IsManaged_Mask = Type_Mask & ~0x10000, - Tag_Shift = 32 - }; - enum ValueType { - Undefined_Type = Immediate_Mask | 0x00000, - Null_Type = Immediate_Mask | 0x10000, - Boolean_Type = Immediate_Mask | 0x20000, - Integer_Type = Immediate_Mask | 0x30000, - Object_Type = NotDouble_Mask | 0x00000, - String_Type = NotDouble_Mask | 0x10000, - Deleted_Type = NotDouble_Mask | 0x30000 - }; - - enum ImmediateFlags { - ConvertibleToInt = Immediate_Mask | 0x1 - }; - - enum ValueTypeInternal { - _Undefined_Type = Undefined_Type, - _Empty_Type = Deleted_Type, - _Null_Type = Null_Type | ConvertibleToInt, - _Boolean_Type = Boolean_Type | ConvertibleToInt, - _Integer_Type = Integer_Type | ConvertibleToInt, - _Object_Type = Object_Type, - _String_Type = String_Type - - }; - - inline unsigned type() const { - return tag & Type_Mask; - } - - // used internally in property - inline bool isEmpty() const { return tag == _Empty_Type; } - - inline bool isUndefined() const { return tag == _Undefined_Type; } - inline bool isNull() const { return tag == _Null_Type; } - inline bool isBoolean() const { return tag == _Boolean_Type; } - inline bool isInteger() const { return tag == _Integer_Type; } - inline bool isDouble() const { return (tag & NotDouble_Mask) != NotDouble_Mask; } - inline bool isNumber() const { return tag == _Integer_Type || (tag & NotDouble_Mask) != NotDouble_Mask; } -#if QT_POINTER_SIZE == 8 - inline bool isString() const { return (tag & Type_Mask) == String_Type; } - inline bool isObject() const { return (tag & Type_Mask) == Object_Type; } -#else - inline bool isString() const { return tag == String_Type; } - inline bool isObject() const { return tag == Object_Type; } -#endif - inline bool isManaged() const { return (tag & IsManaged_Mask) == Object_Type; } - inline bool isConvertibleToInt() const { return (tag & ConvertibleToInt) == ConvertibleToInt; } - inline bool isInt32() { - if (tag == _Integer_Type) - return true; - if (isDouble()) { - int i = (int)dbl; - if (i == dbl) { - int_32 = i; - tag = _Integer_Type; - return true; - } - } - return false; - } - - bool booleanValue() const { - return int_32; - } - double doubleValue() const { - return dbl; - } - void setDouble(double d) { - dbl = d; - } - double asDouble() const { - if (tag == _Integer_Type) - return int_32; - return dbl; - } - int integerValue() const { - return int_32; - } - -#if QT_POINTER_SIZE == 8 - String *stringValue() const { - return (String *)(val & ~(quint64(Type_Mask) << Tag_Shift)); - } - Object *objectValue() const { - return (Object *)(val & ~(quint64(Type_Mask) << Tag_Shift)); - } - Managed *managed() const { - return (Managed *)(val & ~(quint64(Type_Mask) << Tag_Shift)); - } -#else - String *stringValue() const { - return s; - } - Object *objectValue() const { - return o; - } - Managed *managed() const { - return m; - } -#endif - - quint64 rawValue() const { - return val; - } - - static Value emptyValue(); - static Value undefinedValue(); - static Value nullValue(); - static Value fromBoolean(Bool b); - static Value fromDouble(double d); - static Value fromInt32(int i); - static Value fromUInt32(uint i); - static Value fromString(String *s); - static Value fromObject(Object *o); - -#ifndef QMLJS_LLVM_RUNTIME - static Value fromString(ExecutionContext *ctx, const QString &fromString); - static Value fromString(ExecutionEngine *engine, const QString &s); -#endif - - static double toInteger(double fromNumber); - static int toInt32(double value); - static unsigned int toUInt32(double value); - - int toUInt16() const; - int toInt32() const; - unsigned int toUInt32() const; - - bool toBoolean() const; - double toInteger() const; - double toNumber() const; - QString toQString() const; - String *toString(ExecutionContext *ctx) const; - Object *toObject(ExecutionContext *ctx) const; - - inline bool isPrimitive() const { return !isObject(); } -#if QT_POINTER_SIZE == 8 - inline bool integerCompatible() const { - const quint64 mask = quint64(ConvertibleToInt) << 32; - return (val & mask) == mask; - } - static inline bool integerCompatible(Value a, Value b) { - const quint64 mask = quint64(ConvertibleToInt) << 32; - return ((a.val & b.val) & mask) == mask; - } - static inline bool bothDouble(Value a, Value b) { - const quint64 mask = quint64(NotDouble_Mask) << 32; - return ((a.val | b.val) & mask) != mask; - } -#else - inline bool integerCompatible() const { - return (tag & ConvertibleToInt) == ConvertibleToInt; - } - static inline bool integerCompatible(Value a, Value b) { - return ((a.tag & b.tag) & ConvertibleToInt) == ConvertibleToInt; - } - static inline bool bothDouble(Value a, Value b) { - return ((a.tag | b.tag) & NotDouble_Mask) != NotDouble_Mask; - } -#endif - inline bool tryIntegerConversion() { - bool b = isConvertibleToInt(); - if (b) - tag = _Integer_Type; - return b; - } - - String *asString() const; - Managed *asManaged() const; - Object *asObject() const; - FunctionObject *asFunctionObject() const; - BooleanObject *asBooleanObject() const; - NumberObject *asNumberObject() const; - StringObject *asStringObject() const; - DateObject *asDateObject() const; - ArrayObject *asArrayObject() const; - ErrorObject *asErrorObject() const; - - template inline T *as() const; - - uint asArrayIndex() const; - uint asArrayLength(bool *ok) const; - - Value property(ExecutionContext *ctx, String *name) const; - - inline ExecutionEngine *engine() const; - - // Section 9.12 - bool sameValue(Value other) const; - - inline void mark() const; -}; - -} - -QT_END_NAMESPACE - -#endif // QV4VALUE_DEF_P_H diff --git a/src/qml/qml/v4/qv4value_p.h b/src/qml/qml/v4/qv4value_p.h deleted file mode 100644 index 2a783ed34b..0000000000 --- a/src/qml/qml/v4/qv4value_p.h +++ /dev/null @@ -1,426 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QMLJS_VALUE_H -#define QMLJS_VALUE_H - -#include // this HAS to come - -#include -#include -#include "qv4global_p.h" -#include "qv4string_p.h" -#include -#include "qv4managed_p.h" - -//#include - -#include "qv4value_def_p.h" - -QT_BEGIN_NAMESPACE - -namespace QV4 { - -double __qmljs_to_number(const QV4::Value &value); -Q_QML_EXPORT QV4::String *__qmljs_convert_to_string(QV4::ExecutionContext *ctx, const QV4::Value &value); -QV4::Object *__qmljs_convert_to_object(QV4::ExecutionContext *ctx, const QV4::Value &value); - - -inline ExecutionEngine *Value::engine() const { - Managed *m = asManaged(); - return m ? m->engine() : 0; -} - -inline void Value::mark() const { - Managed *m = asManaged(); - if (m) - m->mark(); -} - -inline Value Value::undefinedValue() -{ - Value v; -#if QT_POINTER_SIZE == 8 - v.val = quint64(_Undefined_Type) << Tag_Shift; -#else - v.tag = _Undefined_Type; - v.int_32 = 0; -#endif - return v; -} - -inline Value Value::nullValue() -{ - Value v; -#if QT_POINTER_SIZE == 8 - v.val = quint64(_Null_Type) << Tag_Shift; -#else - v.tag = _Null_Type; - v.int_32 = 0; -#endif - return v; -} - -inline Value Value::emptyValue() -{ - Value v; - v.tag = Value::_Empty_Type; - v.uint_32 = 0; - return v; -} - - -inline Value Value::fromBoolean(Bool b) -{ - Value v; - v.tag = _Boolean_Type; - v.int_32 = (bool)b; - return v; -} - -inline Value Value::fromDouble(double d) -{ - Value v; - v.dbl = d; - return v; -} - -inline Value Value::fromInt32(int i) -{ - Value v; - v.tag = _Integer_Type; - v.int_32 = i; - return v; -} - -inline Value Value::fromUInt32(uint i) -{ - Value v; - if (i < INT_MAX) { - v.tag = _Integer_Type; - v.int_32 = (int)i; - } else { - v.dbl = i; - } - return v; -} - -inline Value Value::fromString(String *s) -{ - Value v; -#if QT_POINTER_SIZE == 8 - v.val = (quint64)s; - v.val |= quint64(_String_Type) << Tag_Shift; -#else - v.tag = _String_Type; - v.s = s; -#endif - return v; -} - -inline Value Value::fromObject(Object *o) -{ - Value v; -#if QT_POINTER_SIZE == 8 - v.val = (quint64)o; - v.val |= quint64(_Object_Type) << Tag_Shift; -#else - v.tag = _Object_Type; - v.o = o; -#endif - return v; -} - -inline bool Value::toBoolean() const -{ - switch (type()) { - case Value::Undefined_Type: - case Value::Null_Type: - return false; - case Value::Boolean_Type: - case Value::Integer_Type: - return (bool)int_32; - case Value::String_Type: - return stringValue()->toQString().length() > 0; - case Value::Object_Type: - return true; - default: // double - if (! doubleValue() || std::isnan(doubleValue())) - return false; - return true; - } -} - -inline Object *Value::toObject(ExecutionContext *ctx) const -{ - if (isObject()) - return objectValue(); - return __qmljs_convert_to_object(ctx, *this); -} - -inline int Value::toInt32() const -{ - if (isConvertibleToInt()) - return int_32; - double d; - if (isDouble()) - d = dbl; - else - d = __qmljs_to_number(*this); - - const double D32 = 4294967296.0; - const double D31 = D32 / 2.0; - - if ((d >= -D31 && d < D31)) - return static_cast(d); - - return Value::toInt32(__qmljs_to_number(*this)); -} - -inline unsigned int Value::toUInt32() const -{ - if (isConvertibleToInt()) - return (unsigned) int_32; - double d; - if (isDouble()) - d = dbl; - else - d = __qmljs_to_number(*this); - - const double D32 = 4294967296.0; - if (dbl >= 0 && dbl < D32) - return static_cast(dbl); - return toUInt32(d); -} - -inline uint Value::asArrayIndex() const -{ - if (isInteger() && int_32 >= 0) - return (uint)int_32; - if (!isDouble()) - return UINT_MAX; - uint idx = (uint)dbl; - if (idx != dbl) - return UINT_MAX; - return idx; -} - -inline uint Value::asArrayLength(bool *ok) const -{ - *ok = true; - if (isConvertibleToInt() && int_32 >= 0) - return (uint)int_32; - if (isDouble()) { - uint idx = (uint)dbl; - if ((double)idx != dbl) { - *ok = false; - return UINT_MAX; - } - return idx; - } - if (isString()) - return stringValue()->toUInt(ok); - - uint idx = toUInt32(); - double d = toNumber(); - if (d != idx) { - *ok = false; - return UINT_MAX; - } - return idx; -} - -inline String *Value::asString() const -{ - if (isString()) - return stringValue(); - return 0; -} - -inline Managed *Value::asManaged() const -{ - if (isManaged()) - return managed(); - return 0; -} - -inline Object *Value::asObject() const -{ - return isObject() ? objectValue() : 0; -} - -inline FunctionObject *Value::asFunctionObject() const -{ - return isObject() ? managed()->asFunctionObject() : 0; -} - -inline BooleanObject *Value::asBooleanObject() const -{ - return isObject() ? managed()->asBooleanObject() : 0; -} - -inline NumberObject *Value::asNumberObject() const -{ - return isObject() ? managed()->asNumberObject() : 0; -} - -inline StringObject *Value::asStringObject() const -{ - return isObject() ? managed()->asStringObject() : 0; -} - -inline DateObject *Value::asDateObject() const -{ - return isObject() ? managed()->asDateObject() : 0; -} - -inline ArrayObject *Value::asArrayObject() const -{ - return isObject() ? managed()->asArrayObject() : 0; -} - -inline ErrorObject *Value::asErrorObject() const -{ - return isObject() ? managed()->asErrorObject() : 0; -} - -// ### -inline Value Managed::construct(Value *args, int argc) { - return vtbl->construct(this, args, argc); -} -inline Value Managed::call(const Value &thisObject, Value *args, int argc) { - return vtbl->call(this, thisObject, args, argc); -} - -struct PersistentValuePrivate -{ - PersistentValuePrivate(const Value &v, ExecutionEngine *engine = 0, bool weak = false); - virtual ~PersistentValuePrivate(); - Value value; - uint refcount; - QV4::ExecutionEngine *engine; - PersistentValuePrivate **prev; - PersistentValuePrivate *next; - - void removeFromList(); - void ref() { ++refcount; } - void deref(); - PersistentValuePrivate *detach(const QV4::Value &value, bool weak = false); - - bool checkEngine(QV4::ExecutionEngine *otherEngine) { - if (!engine) { - Q_ASSERT(!value.isObject()); - engine = otherEngine; - } - return (engine == otherEngine); - } -}; - -class Q_QML_EXPORT PersistentValue -{ -public: - PersistentValue() : d(0) {} - PersistentValue(const Value &val); - PersistentValue(const PersistentValue &other); - PersistentValue &operator=(const PersistentValue &other); - PersistentValue &operator=(const Value &other); - ~PersistentValue(); - - Value value() const { - return d ? d->value : Value::emptyValue(); - } - - ExecutionEngine *engine() { - if (!d) - return 0; - Managed *m = d->value.asManaged(); - return m ? m->engine() : 0; - } - - operator Value() const { return value(); } - - bool isEmpty() const { return !d || d->value.isEmpty(); } - void clear() { - *this = PersistentValue(); - } - -private: - PersistentValuePrivate *d; -}; - -class Q_QML_EXPORT WeakValue -{ -public: - WeakValue() : d(0) {} - WeakValue(const Value &val); - WeakValue(const WeakValue &other); - WeakValue &operator=(const WeakValue &other); - WeakValue &operator=(const Value &other); - ~WeakValue(); - - Value value() const { - return d ? d->value : Value::emptyValue(); - } - - ExecutionEngine *engine() { - if (!d) - return 0; - Managed *m = d->value.asManaged(); - return m ? m->engine() : 0; - } - - operator Value() const { return value(); } - - bool isEmpty() const { return !d || d->value.isEmpty(); } - void clear() { - *this = WeakValue(); - } - - void markOnce(); - -private: - PersistentValuePrivate *d; -}; - -} // namespace QV4 - -QT_END_NAMESPACE - -#endif diff --git a/src/qml/qml/v4/qv4variantobject.cpp b/src/qml/qml/v4/qv4variantobject.cpp deleted file mode 100644 index f18c5b582e..0000000000 --- a/src/qml/qml/v4/qv4variantobject.cpp +++ /dev/null @@ -1,203 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4variantobject_p.h" -#include "qv4functionobject_p.h" -#include "qv4objectproto_p.h" -#include -#include - -QT_BEGIN_NAMESPACE - -using namespace QV4; - -DEFINE_MANAGED_VTABLE(VariantObject); - -VariantObject::VariantObject(ExecutionEngine *engine, const QVariant &value) - : Object(engine) - , ExecutionEngine::ScarceResourceData(value) - , m_vmePropertyReferenceCount(0) -{ - vtbl = &static_vtbl; - prototype = engine->variantPrototype; - if (isScarce()) - internalClass->engine->scarceResources.insert(this); -} - -QVariant VariantObject::toVariant(const QV4::Value &v) -{ - if (Object *o = v.asObject()) - return o->engine()->v8Engine->variantFromJS(v); - - if (v.isString()) - return QVariant(v.stringValue()->toQString()); - if (v.isBoolean()) - return QVariant(v.booleanValue()); - if (v.isNumber()) { - QV4::Value val = v; - if (val.isInt32()) - return QVariant(val.integerValue()); - return QVariant(v.asDouble()); - } - if (v.isNull()) - return QVariant(QMetaType::VoidStar, 0); - assert (v.isUndefined() || v.isEmpty()); - return QVariant(); -} - -bool VariantObject::isScarce() const -{ - QVariant::Type t = data.type(); - return t == QVariant::Pixmap || t == QVariant::Image; -} - -void VariantObject::destroy(Managed *that) -{ - VariantObject *v = static_cast(that); - if (v->isScarce()) - v->node.remove(); - v->~VariantObject(); -} - -bool VariantObject::isEqualTo(Managed *m, Managed *other) -{ - QV4::VariantObject *lv = m->as(); - assert(lv); - - if (QV4::VariantObject *rv = other->as()) - return lv->data == rv->data; - - if (QV4::QmlValueTypeWrapper *v = other->as()) - return v->isEqual(lv->data); - - return false; -} - -void VariantObject::addVmePropertyReference() -{ - if (isScarce() && ++m_vmePropertyReferenceCount == 1) { - // remove from the ep->scarceResources list - // since it is now no longer eligible to be - // released automatically by the engine. - node.remove(); - } -} - -void VariantObject::removeVmePropertyReference() -{ - if (isScarce() && --m_vmePropertyReferenceCount == 0) { - // and add to the ep->scarceResources list - // since it is now eligible to be released - // automatically by the engine. - internalClass->engine->scarceResources.insert(this); - } -} - - -VariantPrototype::VariantPrototype(ExecutionEngine *engine) - : VariantObject(engine, QVariant()) -{ - prototype = engine->objectPrototype; -} - -void VariantPrototype::init(ExecutionEngine *engine) -{ - defineDefaultProperty(engine, QStringLiteral("preserve"), method_preserve, 0); - defineDefaultProperty(engine, QStringLiteral("destroy"), method_destroy, 0); - defineDefaultProperty(engine, QStringLiteral("valueOf"), method_valueOf, 0); - defineDefaultProperty(engine, QStringLiteral("toString"), method_toString, 0); -} - -QV4::Value VariantPrototype::method_preserve(SimpleCallContext *ctx) -{ - VariantObject *o = ctx->thisObject.as(); - if (o && o->isScarce()) - o->node.remove(); - return Value::undefinedValue(); -} - -QV4::Value VariantPrototype::method_destroy(SimpleCallContext *ctx) -{ - VariantObject *o = ctx->thisObject.as(); - if (o) { - if (o->isScarce()) - o->node.remove(); - o->data = QVariant(); - } - return QV4::Value::undefinedValue(); -} - -QV4::Value VariantPrototype::method_toString(SimpleCallContext *ctx) -{ - VariantObject *o = ctx->thisObject.as(); - if (!o) - return Value::undefinedValue(); - QString result = o->data.toString(); - if (result.isEmpty() && !o->data.canConvert(QVariant::String)) - result = QString::fromLatin1("QVariant(%0)").arg(QString::fromLatin1(o->data.typeName())); - return Value::fromString(ctx->engine->newString(result)); -} - -QV4::Value VariantPrototype::method_valueOf(SimpleCallContext *ctx) -{ - VariantObject *o = ctx->thisObject.as(); - if (o) { - QVariant v = o->data; - switch (v.type()) { - case QVariant::Invalid: - return Value::undefinedValue(); - case QVariant::String: - return Value::fromString(ctx->engine->newString(v.toString())); - case QVariant::Int: - return Value::fromInt32(v.toInt()); - case QVariant::Double: - case QVariant::UInt: - return Value::fromDouble(v.toDouble()); - case QVariant::Bool: - return Value::fromBoolean(v.toBool()); - default: - break; - } - } - return ctx->thisObject; -} - -QT_END_NAMESPACE diff --git a/src/qml/qml/v4/qv4variantobject_p.h b/src/qml/qml/v4/qv4variantobject_p.h deleted file mode 100644 index 876539aae1..0000000000 --- a/src/qml/qml/v4/qv4variantobject_p.h +++ /dev/null @@ -1,102 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QV4VARIANTOBJECT_P_H -#define QV4VARIANTOBJECT_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include -#include -#include - -#include -#include - -QT_BEGIN_NAMESPACE - -namespace QV4 { - -struct VariantObject : Object, public ExecutionEngine::ScarceResourceData -{ - Q_MANAGED -public: - VariantObject(ExecutionEngine *engine, const QVariant &value); - - static QVariant toVariant(const QV4::Value &v); - - void addVmePropertyReference(); - void removeVmePropertyReference(); - bool isScarce() const; - int m_vmePropertyReferenceCount; - - static void destroy(Managed *that); - static bool isEqualTo(Managed *m, Managed *other); -}; - -struct VariantPrototype : VariantObject -{ -public: - VariantPrototype(ExecutionEngine *engine); - - void init(ExecutionEngine *engine); - - static Value method_preserve(SimpleCallContext *ctx); - static Value method_destroy(SimpleCallContext *ctx); - static Value method_toString(SimpleCallContext *ctx); - static Value method_valueOf(SimpleCallContext *ctx); -}; - -} - -QT_END_NAMESPACE - -#endif - diff --git a/src/qml/qml/v4/v4.pri b/src/qml/qml/v4/v4.pri deleted file mode 100644 index 4e230f42e0..0000000000 --- a/src/qml/qml/v4/v4.pri +++ /dev/null @@ -1,161 +0,0 @@ -include(../../../3rdparty/masm/masm-defs.pri) - -CONFIG += exceptions - -CONFIG += warn_off - -INCLUDEPATH += $$PWD -INCLUDEPATH += $$OUT_PWD - -SOURCES += \ - $$PWD/qv4codegen.cpp \ - $$PWD/qv4ssa.cpp \ - $$PWD/qv4jsir.cpp \ - $$PWD/qv4engine.cpp \ - $$PWD/qv4context.cpp \ - $$PWD/qv4runtime.cpp \ - $$PWD/qv4value.cpp \ - $$PWD/qv4syntaxchecker.cpp \ - $$PWD/qv4isel_masm.cpp \ - $$PWD/qv4isel_p.cpp \ - $$PWD/qv4debugging.cpp \ - $$PWD/qv4lookup.cpp \ - $$PWD/qv4identifier.cpp \ - $$PWD/qv4identifiertable.cpp \ - $$PWD/qv4mm.cpp \ - $$PWD/qv4managed.cpp \ - $$PWD/qv4internalclass.cpp \ - $$PWD/qv4sparsearray.cpp \ - $$PWD/qv4arrayobject.cpp \ - $$PWD/qv4argumentsobject.cpp \ - $$PWD/qv4booleanobject.cpp \ - $$PWD/qv4dateobject.cpp \ - $$PWD/qv4errorobject.cpp \ - $$PWD/qv4function.cpp \ - $$PWD/qv4functionobject.cpp \ - $$PWD/qv4globalobject.cpp \ - $$PWD/qv4jsonobject.cpp \ - $$PWD/qv4mathobject.cpp \ - $$PWD/qv4numberobject.cpp \ - $$PWD/qv4object.cpp \ - $$PWD/qv4objectproto.cpp \ - $$PWD/qv4regexpobject.cpp \ - $$PWD/qv4stringobject.cpp \ - $$PWD/qv4variantobject.cpp \ - $$PWD/qv4string.cpp \ - $$PWD/qv4objectiterator.cpp \ - $$PWD/qv4regexp.cpp \ - $$PWD/qv4unwindhelper.cpp \ - $$PWD/qv4serialize.cpp \ - $$PWD/qv4script.cpp \ - $$PWD/qv4executableallocator.cpp \ - $$PWD/qv4sequenceobject.cpp \ - $$PWD/qv4include.cpp \ - $$PWD/qv4qobjectwrapper.cpp \ - $$PWD/qv4qmlextensions.cpp \ - $$PWD/qv4stacktrace.cpp \ - $$PWD/qv4exception.cpp - -HEADERS += \ - $$PWD/qv4global_p.h \ - $$PWD/qv4codegen_p.h \ - $$PWD/qv4ssa_p.h \ - $$PWD/qv4jsir_p.h \ - $$PWD/qv4engine_p.h \ - $$PWD/qv4context_p.h \ - $$PWD/qv4runtime_p.h \ - $$PWD/qv4math_p.h \ - $$PWD/qv4value_p.h \ - $$PWD/qv4value_def_p.h \ - $$PWD/qv4syntaxchecker_p.h \ - $$PWD/qv4isel_masm_p.h \ - $$PWD/qv4isel_p.h \ - $$PWD/qv4isel_util_p.h \ - $$PWD/qv4debugging_p.h \ - $$PWD/qv4lookup_p.h \ - $$PWD/qv4identifier_p.h \ - $$PWD/qv4identifiertable_p.h \ - $$PWD/qv4mm_p.h \ - $$PWD/qv4managed_p.h \ - $$PWD/qv4internalclass_p.h \ - $$PWD/qv4sparsearray_p.h \ - $$PWD/qv4arrayobject_p.h \ - $$PWD/qv4argumentsobject_p.h \ - $$PWD/qv4booleanobject_p.h \ - $$PWD/qv4dateobject_p.h \ - $$PWD/qv4errorobject_p.h \ - $$PWD/qv4function_p.h \ - $$PWD/qv4functionobject_p.h \ - $$PWD/qv4globalobject_p.h \ - $$PWD/qv4jsonobject_p.h \ - $$PWD/qv4mathobject_p.h \ - $$PWD/qv4numberobject_p.h \ - $$PWD/qv4object_p.h \ - $$PWD/qv4objectproto_p.h \ - $$PWD/qv4regexpobject_p.h \ - $$PWD/qv4stringobject_p.h \ - $$PWD/qv4variantobject_p.h \ - $$PWD/qv4string_p.h \ - $$PWD/qv4property_p.h \ - $$PWD/qv4objectiterator_p.h \ - $$PWD/qv4regexp_p.h \ - $$PWD/qv4unwindhelper_p.h \ - $$PWD/qv4unwindhelper_p-dw2.h \ - $$PWD/qv4unwindhelper_p-arm.h \ - $$PWD/qv4serialize_p.h \ - $$PWD/qv4script_p.h \ - $$PWD/qv4util_p.h \ - $$PWD/qv4executableallocator_p.h \ - $$PWD/qv4sequenceobject_p.h \ - $$PWD/qv4include_p.h \ - $$PWD/qv4qobjectwrapper_p.h \ - $$PWD/qv4qmlextensions_p.h \ - $$PWD/qv4stacktrace_p.h \ - $$PWD/qv4exception_p.h - -# Use SSE2 floating point math on 32 bit instead of the default -# 387 to make test results pass on 32 and on 64 bit builds. -linux-g++*:isEqual(QT_ARCH,i386) { - QMAKE_CFLAGS += -march=pentium4 -msse2 -mfpmath=sse - QMAKE_CXXFLAGS += -march=pentium4 -msse2 -mfpmath=sse -} - -linux*|mac { - LIBS += -ldl -} - -# Only on Android/ARM at the moment, because only there we have issues -# replacing __gnu_Unwind_Find_exidx with our own implementation, -# and thus require static libgcc linkage. -android:equals(QT_ARCH, "arm"):*g++* { - static_libgcc = $$system($$QMAKE_CXX -print-file-name=libgcc.a) - LIBS += $$static_libgcc - SOURCES += $$PWD/qv4exception_gcc.cpp - DEFINES += V4_CXX_ABI_EXCEPTION -} - -debug-with-libunwind { - UW_INC=$$(LIBUNWIND_INCLUDES) - isEmpty(UW_INC): error("Please set LIBUNWIND_INCLUDES") - INCLUDEPATH += $$UW_INC - UW_LIBS=$$(LIBUNWIND_LIBS) - isEmpty(UW_LIBS): error("Please set LIBUNWIND_LIBS") - LIBS += -L$$UW_LIBS - equals(QT_ARCH, arm): LIBS += -lunwind-arm - LIBS += -lunwind-dwarf-common -lunwind-dwarf-local -lunwind-elf32 -lunwind - DEFINES += WTF_USE_LIBUNWIND_DEBUG=1 -} - -valgrind { - DEFINES += V4_USE_VALGRIND -} - -ios: DEFINES += ENABLE_ASSEMBLER_WX_EXCLUSIVE=1 - -win32 { - LIBS_PRIVATE += -lDbgHelp -} - -include(moth/moth.pri) -include(../../../3rdparty/masm/masm.pri) -include(../../../3rdparty/double-conversion/double-conversion.pri) -- cgit v1.2.3